文章目录
一、DispatcherServlet注册到Tomcat
前面说到,DispatcherServlet是通过SpringBoot的自动配置来加载的,但是该类除了直接实例化到Spring的ioc容器中,还要注册到Tomcat中(通过ServletRegistrationBean的形式),当tomcat启动时,初始化该类,启动mvc子容器。
1.1、SpringBoot的自动配置
DispatcherServlet加载到Tomcat中,依然还是靠SpringBoot的自动配置类DispatcherServletAutoConfiguration
。
实际上该类的逻辑就是定义了四个类,分别是:
- DispatcherServletRegistrationCondition:决定是否注册DispatcherServlet到Servlet容器,beanFactory中有,servlet中没有,则加载
- DefaultDispatcherServletCondition:决定是否加载DispatcherServlet
- DispatcherServletRegistrationConfiguration:负责注册DispatcherServlet到Servlet容器,并进行一些设置。
- 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 流程总结
- 通过Springboot自动配置,将DispatcherServlet和DispatcherServletRegistrationBean创建出来,然后将DispatcherServlet及其处理路径封装到DispatcherServletRegistrationBean对象中,然后加入到ioc容器中。
- SpringBoot继续启动,在onRefresh()方法中,创建Tomcat实例,通过lambda表达式的形式生成函数接口ServletContextInitializer的一个实现类(暂时命名为A)作为参数传递到Tomcat中。该ServletContextInitializer的逻辑是调用ServeletWebServerApplicationContext的selfInitialize方法,该方法会从ioc容器找到我们配置的真正的那些ServletContextInitializer。
- A会被注册到对应StandardContext中
- 当tomcat容器启动时,会调用ServletContextInitializer(A)的onStartup方法,然后A的逻辑是调用ServeletWebServerApplicationContext的selfInitialize方法,从ioc容器中找到所有的ServletContextInitializer,然后再依次调用这些类的onStratup方法。
- DispatcherServletRegistrationBean实现了ServletContextInitializer接口,其onStartup方法会将DispatcherServlet注册到servletContext中。(ServletContext是StandardContext下供所有Servlet使用的上下文)
二、SpringMVC启动
参考:
从Tomcat到SpringMVC的2.3节
后期再补充详细的MVC启动流程的细节。
主要逻辑就是DispatcherServlet的onStartup方法中。