美文网首页
SpringMVC框架的几种初始化途径

SpringMVC框架的几种初始化途径

作者: 一根线条 | 来源:发表于2021-05-08 23:09 被阅读0次

    1,首先我们来看看spring-web与spring-webmvc的关系

    `spring-web` provides core HTTP integration, including some handy Servlet filters, Spring HTTP Invoker, infrastructure to integrate with other web frameworks and HTTP technologies e.g. Hessian, Burlap.
    
    `spring-webmvc` is an implementation of Spring MVC. `spring-webmvc` [depends on](http://repo1.maven.org/maven2/org/springframework/spring-webmvc/3.1.3.RELEASE/spring-webmvc-3.1.3.RELEASE.pom) on `spring-web`, thus including it will transitively add `spring-web`. You don't have to add `spring-web`explicitly.
    
    You should depend only on `spring-web` if you don't use Spring MVC but want to take advantage of other web-related technologies that Spring supports.
    

    2,可完成初始化Springmvc的几种途径

    spring-mvc 基本逻辑是通过DispatcherServlet来处理客户端的http请求,所以其核心是要将DispatcherServlet添加到Servlet容器的Servlet链中。

    2.1,在web.xml中配置DispatcherServlet

    使用父ApplicationContext管理所有bean的方式

    <web-app>
    
        <!-- 通过Listener在容器启动后创建ApplicationContext,并作为父级上下文 -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <!-- 指定Listener中创建ApplicationContext所使用的配置文件-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app-context.xml</param-value>
        </context-param>
    
        <!-- 配置spring webmvc执行特定请求的servlet【可以配置多个的哦】-->
        <servlet>
            <servlet-name>app</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <!-- 不指定子ApplicationContext初始化使用的配置文件-->
                <param-name>contextConfigLocation</param-name>
                <param-value></param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>app</servlet-name>
            <url-pattern>/app/*</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    使用父ApplicationContext管理公共bean(例如:Service,Dao等),使用子ApplicationContext管理与web相关bean(例如:Controller,HandlerMapping等)的方式。

    <web-app>
    
        <!-- 通过Listener在容器启动后创建ApplicationContext,并作为父级上下文 -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <!-- 指定Listener中创建ApplicationContext所使用的配置文件-->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/root-context.xml</param-value>
        </context-param>
    
       <!-- 配置spring webmvc执行特定请求的servlet【可以配置多个的哦】-->
        <servlet>
            <servlet-name>app1</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <!-- 指定了子ApplicationContext初始化使用的配置文件-->
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/app1-context.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>app1</servlet-name>
            <url-pattern>/app1/*</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    更多可参考:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html

    2.2,通过Servlet3+规范来初始化

    从Servlet3.0开始我们可以通过编程的方式来配置servlet或filter。第一种方法是通过ServletContextListener监听器来添加;另一种是通过ServletContainerInitializer接口来添加。

    2.2.1 通过Listener来添加
    @WebListener
    public class InitServletContextListener implements ServletContextListener {
    
        @Override
        public void contextInitialized(ServletContextEvent contextEvent) {
            ServletContext context = contextEvent.getServletContext() ;
            //添加过滤器
            FilterRegistration.Dynamic filterRegistration = context.addFilter("filterName","className") ;
            //filterRegistration.addMappingForUrlPatterns();
            //添加servlet
            ServletRegistration.Dynamic servletRegistration = context.addServlet("servletName","className") ;
            servletRegistration.addMapping("/*") ;
            servletRegistration.setInitParameter("name","custom") ;
            servletRegistration.setLoadOnStartup(1);
            //servletRegistration.setMultipartConfig();
    
            //添加监听器
            context.addListener(HttpSessionIdListener.class);
        }
    }
    
    2.2.2 通过ServletContainerInitializer来添加

    在spring-web\META-INF\services\javax.servlet.ServletContainerInitializer文件中配置了一个实现类org.springframework.web.SpringServletContainerInitializer。

    通过Java提供的SPI机制,Servlet容器可以得到jar包中配置的ServletContainerInitializer实现类,然后通过Servlet容器自己的ServletContainerInitializer实现类来调用通过SPI机制得到的ServletContainerInitializer实现类的实例。

    例如在Tomcat中就有一个ServletContainerInitializer 的实现类TomcatStarter :

    /**
     * {@link ServletContainerInitializer} used to trigger {@link ServletContextInitializer
     * ServletContextInitializers} and track startup errors.
     *
     * @author Phillip Webb
     * @author Andy Wilkinson
     */
    class TomcatStarter implements ServletContainerInitializer {
    
        private static final Log logger = LogFactory.getLog(TomcatStarter.class);
    
        private final ServletContextInitializer[] initializers;
    
        private volatile Exception startUpException;
    
        TomcatStarter(ServletContextInitializer[] initializers) {
            this.initializers = initializers;
        }
    
        @Override
        public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
            try {
                for (ServletContextInitializer initializer : this.initializers) {
                    initializer.onStartup(servletContext);
                }
            }
            catch (Exception ex) {
                this.startUpException = ex;
                // Prevent Tomcat from logging and re-throwing when we know we can
                // deal with it in the main thread, but log for information here.
                if (logger.isErrorEnabled()) {
                    logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
                            + ex.getMessage());
                }
            }
        }
    
        Exception getStartUpException() {
            return this.startUpException;
        }
    }
    

    期间会调用SpringServletContainerInitializer实例对象的onStartup方法。

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
                throws ServletException {
    
            List<WebApplicationInitializer> initializers = new LinkedList<>();
    
            if (webAppInitializerClasses != null) {
                for (Class<?> waiClass : webAppInitializerClasses) {
                    // Be defensive: Some servlet containers provide us with invalid classes,
                    // no matter what @HandlesTypes says...
                    if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
                            WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
                        try {
                            initializers.add((WebApplicationInitializer)
                                    ReflectionUtils.accessibleConstructor(waiClass).newInstance());
                        }
                        catch (Throwable ex) {
                            throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
                        }
                    }
                }
            }
    
            if (initializers.isEmpty()) {
                servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
                return;
            }
    
            servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
            AnnotationAwareOrderComparator.sort(initializers);
            for (WebApplicationInitializer initializer : initializers) {
                initializer.onStartup(servletContext);
            }
        }
    }
    

    SpringServletContainerInitializer作为了WebApplicationInitializer类型的委托,由于该类添加了 @HandlesTypes(WebApplicationInitializer.class) 注解,所以servlet容器会扫描类路径下WebApplicationInitializer类型的实现类,并将所有的实现类放入Set集合中传递给方法的参数。

    那么只要提供一个WebApplicationInitializer的实现类便可以驱动spring webmvc框架的初始化了。

    image.png

    2.3,通过ServletRegistrationBean来完成初始化

    在前面的章节《SpringBoot内嵌Servlet容器启动过程》中有讲过在springboot中servlet容器的启动过程,我们可以通过 org.springframework.boot.web.servlet.ServletContextInitializer 的子类来完成spring-webmvc的初始化工作,而其子类DispatcherServletRegistrationBean便可以很好的完成将DispatcherServlet注册到servlet链的工作。

    在spring-boot-autoconfigure-2.4.1.jar!\META-INF\spring.factories文件中有几个有关web的自动状态的配置类

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    rg.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
    
    org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
    org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
    

    而其中的DispatcherServletAutoConfiguration配置类并可完成DispatcherServletRegistrationBean创建,从而实现DispatcherServlet的注册。

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(DispatcherServlet.class)
    @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
    public class DispatcherServletAutoConfiguration {
    
        /*
         * The bean name for a DispatcherServlet that will be mapped to the root URL "/"
         */
        public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
    
        /*
         * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
         */
        public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
    
        @Configuration(proxyBeanMethods = false)
        @Conditional(DefaultDispatcherServletCondition.class)
        @ConditionalOnClass(ServletRegistration.class)
        @EnableConfigurationProperties(WebMvcProperties.class)
        protected static class DispatcherServletConfiguration {
    
            @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
            public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
                DispatcherServlet dispatcherServlet = new DispatcherServlet();
                dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
                dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
                dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
                dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
                dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
                return dispatcherServlet;
            }
    
            @Bean
            @ConditionalOnBean(MultipartResolver.class)
            @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
            public MultipartResolver multipartResolver(MultipartResolver resolver) {
                // Detect if the user has created a MultipartResolver but named it incorrectly
                return resolver;
            }
        }
    
        @Configuration(proxyBeanMethods = false)
        @Conditional(DispatcherServletRegistrationCondition.class)
        @ConditionalOnClass(ServletRegistration.class)
        @EnableConfigurationProperties(WebMvcProperties.class)
        @Import(DispatcherServletConfiguration.class)
        protected static class DispatcherServletRegistrationConfiguration {
    
            @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
            @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
            public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
                    WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
                DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
                        webMvcProperties.getServlet().getPath());
                registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
                registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
                multipartConfig.ifAvailable(registration::setMultipartConfig);
                return registration;
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:SpringMVC框架的几种初始化途径

          本文链接:https://www.haomeiwen.com/subject/cglpdltx.html