前言
上篇【SpringBoot深度探究(九)源码探究启动流程之三】说完了Tomcat启动以后,到目前为止SpringBoot的启动流程就剩下一个问题没有解决,那就是如何把DispatcherServlet和Spring环境联系起来的,那么本篇博客将会把这个问题做一个讲解。更多Spring内容进入【Spring解读系列目录】。
DispatcherServletAutoConfiguration
和之前模拟的一样,首先SpringBoot在启动中new出来,然后再把DispatcherServlet注册到Spring容器中。做这一步事情的组件,就是这一段的标题DispatcherServletAutoConfiguration类初始化之后的结果,首先先看这个类上的注解。
DispatcherServletAutoConfiguration注解
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
. . . . . .
}
首先@Configuration
注解,说明这是一个配置类,会在Spring容器启动的时候被加载。然后@ConditionalOnWebApplication
指定这是一个web项目。之后@ConditionalOnClass
,如果发现classpath里面有DispatcherServlet就实例化出来一个bean,这里其实就是在关联Spring环境和DispatcherServlet。最后@AutoConfigureAfter
注解说明此类会在ServletWebServerFactoryAutoConfiguration配置之后再进行。然后往下走:
DispatcherServlet Bean & MultipartResolver Bean
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({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
//这里DispatcherServlet被new出来,并且作为一个Bean加入到容器中去
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
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 = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
/**暂时略**/
}
进来以后可以发现大多数都是static字段标注的,就说明这些内部类会在最初DispatcherServletAutoConfiguration 初始化的时候就执行这些内容,也就是说DispatcherServlet就是在DispatcherServletConfiguration这个静态类块new
出来的。这也是第一个静态内部类块,在里面将DispatcherServlet创建出来并且作为一个bean加入到了Spring容器当中。同时也创建了一个上传模块MultipartResolver 的bean,加入到了Spring容器中。
DispatcherServlet配置
public class DispatcherServletAutoConfiguration {
protected static class DispatcherServletConfiguration {
//见上方,略
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class) //依赖上面的静态内部类
protected static class DispatcherServletRegistrationConfiguration {
//初始化是在这里做的,setName,setLoadOnStartup等等
@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) {
//把DispatcherServlet和Spring环境和关联起来
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
//设置名字,作用就是servletContext.addServlet("myBootServlet", servlet);
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
//设置优先值,作用就是registration.setLoadOnStartup(1);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
//下面两个类是做验证用的,忽略掉
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition { }
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition { }
}
首先看到@Import(DispatcherServletConfiguration.class)
,说明这个类在执行的时候会依赖上面的DispatcherServletConfiguration配置类。当这个配置类运行完了才会执行这里,因为都是静态的,类被载入即执行。在这里面dispatcherServlet
被传入,并且进行初始化。过程是这样的:首先把dispatcherServlet
添加给ServletRegistration.register()
方法,而后又通过SPI技术调用onStartup()
方法内调用register()
方法完成环境绑定。这部分代码跳的太厉害就再贴代码了,为了方便理解画个图示:
后面就是DispatcherServlet的正常配置了,配置Servlet名字,设置设置优先值等等。
总结
到此一个SpringBoot的Web项目从最初的Spring容器启动,到Tomcat启动完毕关联相关的Servlet的流程,至于SpringBoot中那些Listener的作用,这里先挖个坑。笔者有空了一定填上。