Spring MVC(3)------Tomcat加载DispatcherServlet及MVC启动流程

一、DispatcherServlet注册到Tomcat

前面说到,DispatcherServlet是通过SpringBoot的自动配置来加载的,但是该类除了直接实例化到Spring的ioc容器中,还要注册到Tomcat中(通过ServletRegistrationBean的形式),当tomcat启动时,初始化该类,启动mvc子容器。

1.1、SpringBoot的自动配置

DispatcherServlet加载到Tomcat中,依然还是靠SpringBoot的自动配置类DispatcherServletAutoConfiguration

实际上该类的逻辑就是定义了四个类,分别是:

  1. DispatcherServletRegistrationCondition:决定是否注册DispatcherServlet到Servlet容器,beanFactory中有,servlet中没有,则加载
  2. DefaultDispatcherServletCondition:决定是否加载DispatcherServlet
  3. DispatcherServletRegistrationConfiguration:负责注册DispatcherServlet到Servlet容器,并进行一些设置。
  4. DispatcherServletConfiguration:负责加载DispatcherServlet并进行一些配置
@AutoConfigureOrder(-2147483648)
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
//存在DispatcherServlet.class文件才加载
@ConditionalOnClass({DispatcherServlet.class})
//在webServerFactory自动配置完成后才加载,防止工厂还没设置好
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
public class DispatcherServletAutoConfiguration {
    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
    public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

    public DispatcherServletAutoConfiguration() {
    }
    
    //第一个类,决定是否注册DispatcherServlet到web容器
    @Order(2147483637)
    private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
        private DispatcherServletRegistrationCondition() {
        }

        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            ConditionOutcome outcome = this.checkDefaultDispatcherName(beanFactory);
            return !outcome.isMatch() ? outcome : this.checkServletRegistration(beanFactory);
        }

        private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory) {
            List<String> servlets = Arrays.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
            boolean containsDispatcherBean = beanFactory.containsBean("dispatcherServlet");
            return containsDispatcherBean && !servlets.contains("dispatcherServlet") ? ConditionOutcome.noMatch(this.startMessage().found("non dispatcher servlet").items(new Object[]{"dispatcherServlet"})) : ConditionOutcome.match();
        }

        private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory) {
            Builder message = this.startMessage();
            List<String> registrations = Arrays.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));
            boolean containsDispatcherRegistrationBean = beanFactory.containsBean("dispatcherServletRegistration");
            if (registrations.isEmpty()) {
                return containsDispatcherRegistrationBean ? ConditionOutcome.noMatch(message.found("non servlet registration bean").items(new Object[]{"dispatcherServletRegistration"})) : ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());
            } else if (registrations.contains("dispatcherServletRegistration")) {
                return ConditionOutcome.noMatch(message.found("servlet registration bean").items(new Object[]{"dispatcherServletRegistration"}));
            } else {
                return containsDispatcherRegistrationBean ? ConditionOutcome.noMatch(message.found("non servlet registration bean").items(new Object[]{"dispatcherServletRegistration"})) : ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations).append("and none is named dispatcherServletRegistration"));
            }
        }

        private Builder startMessage() {
            return ConditionMessage.forCondition("DispatcherServlet Registration", new Object[0]);
        }
    }
    //第二个类,决定是否创建DispatcherServlet对象
    @Order(2147483637)
    private static class DefaultDispatcherServletCondition extends SpringBootCondition {
        private DefaultDispatcherServletCondition() {
        }

        public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Builder message = ConditionMessage.forCondition("Default DispatcherServlet", new Object[0]);
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            List<String> dispatchServletBeans = Arrays.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
            if (dispatchServletBeans.contains("dispatcherServlet")) {
                return ConditionOutcome.noMatch(message.found("dispatcher servlet bean").items(new Object[]{"dispatcherServlet"}));
            } else if (beanFactory.containsBean("dispatcherServlet")) {
                return ConditionOutcome.noMatch(message.found("non dispatcher servlet bean").items(new Object[]{"dispatcherServlet"}));
            } else {
                return dispatchServletBeans.isEmpty() ? ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll()) : ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans").items(Style.QUOTE, dispatchServletBeans).append("and none is named dispatcherServlet"));
            }
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    //负责将DispatcherServlet注册到servlet容器中,即tomcat中
    //原理是创建一个DispatcherServletRegistrationBean对象
    @Conditional({DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition.class})
    @ConditionalOnClass({ServletRegistration.class})
    @EnableConfigurationProperties({WebMvcProperties.class})
    @Import({DispatcherServletAutoConfiguration.DispatcherServletConfiguration.class})
    protected static class DispatcherServletRegistrationConfiguration {
        protected DispatcherServletRegistrationConfiguration() {
        }

        @Bean(
            name = {"dispatcherServletRegistration"}
        )
        @ConditionalOnBean(
            value = {DispatcherServlet.class},
            name = {"dispatcherServlet"}
        )
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            //创建DispatcherServletRegistrationBean,
            //包含一个DispatcherServlet和一个urlMapping
            //即DispatcherServlet负责处理的请求路径
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
   //负责加载DispatcherServlet
    @Conditional({DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition.class})
    @ConditionalOnClass({ServletRegistration.class})
    @EnableConfigurationProperties({HttpProperties.class, WebMvcProperties.class})
    protected static class DispatcherServletConfiguration {
        protected DispatcherServletConfiguration() {
        }

        @Bean(
            name = {"dispatcherServlet"}
        )
        public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
            dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

        @Bean
        @ConditionalOnBean({MultipartResolver.class})
        @ConditionalOnMissingBean(
            name = {"multipartResolver"}
        )
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
            return resolver;
        }
    }
}

1.2 DispatcherServletRegistrationBean

DispatcherServletRegistrationBean的类图:
在这里插入图片描述
通过类图,可知,其抽象基类是RegistrationBean,该类支持Servlet、Listener、Filter三种类型的Web组件的注册。

RegistrationBean接口实现了ServletContextInitializer接口,该接口在Context容器启动时,会加载到Context中并启动(此时的加载的是指通过配置文件配置的那种,而出内嵌的是通过SpringBoot对应的配置类加载进去的)。

自动配置类将RegistrationBean加载到了beanFactory,那它是如何注入到Servlet容器的呢?
这是通过创建WebServer时传入的。上一篇提到过,
factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});创建web容器
this.getSelfInitializer()就是通过lambda表达式的形式将这个初始化ServletContextInitilizer传入Web容器的(该ServletContextInitilizer主要负责在容器启动时,从ioc容器找到其他ServletContextInitilizer然后调用他们的onStartup方法)在这里插入图片描述
所以当StandardContext容器启动时,调用ServletContextInitializer的onStartup方法,会执行ServletWebServerApplicationContext.selfInitialize()方法
该方法通过getServletContextInitializerBeans()方法得到ServletContextInitializer并调用onStartup()方法,此时DispatcherServletRegistrationBean就在其中,因此DispatcherServlet被注册到Tomcat的StandardContext中。
getServletContextInitializerBeans()的执行逻辑在这里插入图片描述

1.3 流程总结

  1. 通过Springboot自动配置,将DispatcherServlet和DispatcherServletRegistrationBean创建出来,然后将DispatcherServlet及其处理路径封装到DispatcherServletRegistrationBean对象中,然后加入到ioc容器中。
  2. SpringBoot继续启动,在onRefresh()方法中,创建Tomcat实例,通过lambda表达式的形式生成函数接口ServletContextInitializer的一个实现类(暂时命名为A)作为参数传递到Tomcat中。该ServletContextInitializer的逻辑是调用ServeletWebServerApplicationContext的selfInitialize方法,该方法会从ioc容器找到我们配置的真正的那些ServletContextInitializer。
  3. A会被注册到对应StandardContext中
  4. 当tomcat容器启动时,会调用ServletContextInitializer(A)的onStartup方法,然后A的逻辑是调用ServeletWebServerApplicationContext的selfInitialize方法,从ioc容器中找到所有的ServletContextInitializer,然后再依次调用这些类的onStratup方法。
  5. DispatcherServletRegistrationBean实现了ServletContextInitializer接口,其onStartup方法会将DispatcherServlet注册到servletContext中。(ServletContext是StandardContext下供所有Servlet使用的上下文)

二、SpringMVC启动

参考:
从Tomcat到SpringMVC的2.3节

后期再补充详细的MVC启动流程的细节。

主要逻辑就是DispatcherServlet的onStartup方法中。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值