SpringMVC的核心入口就是DispatcherServlet,无论是通过web.xml 还是 JavaConfig的方式配置,都是要遵循Servlet规范的,即想要动态的添加配置一个Servlet本质上最终还是通过 ServletContext.addServlet()
,虽然有了SpringMVC一般很少有需要直接编写Servlet,但是对于理解原理还是很必要的
配置Servlet的方法有许多方式:
1. web.xml静态配置servlet
2. ServletContainerInitializer编码动态注册
3. 注解@WebServlet (v3.0+)
SpringMVC: WebApplicationInitializer
Servlet3提供了ServletContainerInitializer
接口来支持动态的注册Servlet/Filter/Listener
spring-web模块里的SpringServletContainerInitializer
实现并抽象出新的接口:WebApplicationInitializer
所以,它的存在基本替代web.xml配置,在初始化的时候注册并配置容器上下文,官方doc的示例:
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
ac.refresh();
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
为了简化
DispatcherServlet
的基本千篇一律的配置,也提供了AbstractAnnotationConfigDispatcherServletInitializer
直接继承实现对应的配置类列表相关方法即可
SpringBoot: ServletContextInitializer
SpringBoot没有选择hook到Servlet规范里的的生命周期,而是抽象出自己的接口来配置嵌入的容器,把Servlet/Filter/Listener这些变成Spring Bean一样去配置
ServletContainerInitializer
生命周期是归Servlet容器管理
ServletContextInitializer
他是Spring自己管理
SpringBoot应用一般都是embbed server,默认不会去走Servlet那一套,所以想配置Servlet就是通过ServletContextInitializer
或者其子类来实现注册
源码的大概流程
-
SpringApplication.run();
-
ServletWebServerApplicationContext#onRefresh() -> createWebServer()
创建嵌入的servlet容器的时候(默认tomcat),new TomcatWebServer()构造函数里执行了初始化initialize(),也就是日志里比较标志性的一行 Tomcat initialized with port(s): 8080 (http)
- Tomcat#start() -> LifecycleBase#start()
-> StandardService#startInternal() -> StandardEngine#startInternal() -> ContainerBase#startInternal()
然后提交了异步的FutureTask,就是startStopExecutor.submit(new StartChild(children[i])),TomcatEmbeddedContext#start()的时候会执行他的ServletContextInitializer
列表
ServletWebServerApplicationContext里有一个实现的ServletContextInitializer逻辑:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 这里就是完成各种Servlet Filter Listener的注册关键逻辑
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
getServletContextInitializerBeans()
逻辑就是从BeanFactory里获取指定类型的Bean列表,当然这其中就包含了一个DispatcherServletRegistrationBean
,这个Bean的配置是在DispatcherServletAutoConfiguration
里配置的
总结
-
DispatcherServletAutoConfiguration
配置相应的Bean - 内嵌容器start,然后会执行当然上下文里
ServletContextInitializer
类型的Bean完成初始化&配置
网友评论