回忆一下,我们在普通的web项目中是如何使用springMVC的,
首先需要在web.xml中配置DispatcherServlet,
<servlet>
<servlet-name>springMVCservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMVCservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
但是在springboot中没有web.xml文件,那我们如何去配置一个DispatcherServlet?
servlet实例是需要添加/注册到像tomcat这样的servletContext中,才能对外提供服务的。
在Servlet3.0规范中,提供了另外一种添加servlet的方式,
类似这样子:
servletContext.addServlet(name, this.servlet);
在Servlet3.0规范中,
容器启动时会自动扫描所有已添加的jar包下META-INF\services\javax.servlet.ServletContainerInitializer中指定的类的全路径,
实例化该类,然后回调META-INF\services\javax.servlet.ServletContainerInitializer中指定的ServletContainerInitializer的实现类的onStartUp方法。
如果我们能动态地往web容器中添加一个我们构造好的DispatcherServlet对象,就能实现springMVC配置了。
分两步:
1、DispatcherServlet加载到Spring IoC容器;
2、DispatcherServlet注册到servlet容器tomcat
打开spring-boot-2.2.9.RELEASE\spring-boot-project\spring-boot-autoconfigure\src\main\resources\META-INF\spring.factories,
DispatcherServletAutoConfiguration就是DispatcherServlet的自动配置类。
DispatcherServletAutoConfiguration
看下它的内部类,
spring启动时会将DispatcherServlet加载到IoC容器中去。
spring启动时会将DispatcherServletRegistrationBean加载到IoC容器中去,
DispatcherServletRegistrationBean是DispatcherServlet它的一个注册类,
负责将DispatcherServlet注册到ServletContext中。
我们先来看下DispatcherServletRegistrationBean的类关系图,
发现它是ServletContextInitializer的一个实现类,
ServletContextInitializer和我们上边提到过的ServletContainerInitializer的作用类似,
我们看下ServletContainerInitializer,
发现只有一个onStartup方法。
我们根据DispatcherServletRegistrationBean的类关系图查看它的实现方法,
org.springframework.boot.web.servlet.ServletContextInitializer#onStartup
→
org.springframework.boot.web.servlet.RegistrationBean#onStartup
→
org.springframework.boot.web.servlet.DynamicRegistrationBean#register
→
org.springframework.boot.web.servlet.ServletRegistrationBean#addRegistration
终于到了这一步,就是在这儿把DispatcherServlet注册到servlet容器的。
那ServletContainerInitializer.onStartup是在什么时候被调用的?
我们还得回到AbstractApplicationContext#refresh这一步。
SpringBootMytestApplication#main
→
SpringApplication#run(java.lang.Class>, java.lang.String...) → SpringApplication#run(java.lang.Class>[], java.lang.String[])
→
SpringApplication#run(java.lang.String…)
→
SpringApplication#refreshContext
→
SpringApplication#refresh
→
AbstractApplicationContext#refresh
→
ServletWebServerApplicationContext#onRefresh
→
ServletWebServerApplicationContext#createWebServer
→
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getSelfInitializer
→
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
这一步就是获取ServletContextInitializer,执行onStartup方法,
至此,DispatcherServlet就注册到servletContext了。