- Spring Boot实战:从0开始动手搭建企业级项目
- 十三
- 1956字
- 2021-08-13 19:43:40
6.4 DispatcherServlet自动配置流程
DispatcherServletAutoConfiguration自动配置类会在IOC容器中注册DispatcherServlet和DispatcherServletRegistrationConfiguration两个Bean,此为自动配置类的配置结果。那么这个自动配置类何时生效?配置流程又是如何的呢?
这里需要结合Spring Boot的启动流程来进行讲解和介绍。
6.4.1 注册至IOC容器
DispatcherServletAutoConfiguration类的条件注解@AutoConfigureAfter (ServletWebServerFactoryAutoConfiguration.class)定义了自动配置类生效的时间是在ServletWebServerFactory自动配置之后,那么首先要找到这个自动配置流程的生效时间点。
结合前文中SpringApplication.run()方法调用的过程,Spring Boot项目在启动过程中调用了AbstractApplicationContext.refresh()方法,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/114-1.jpg?sign=1739379102-hD8FV94GXVc5W39AYYF1Yc5ShkZfzOv9-0-839627476d0d72a4aa09e7804cae6d6c)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/115-1.jpg?sign=1739379102-WRFGb8jMP8qtdWHHLK6gUU4VRaJlUYYX-0-d8e12d8d0ad5e87c04466d3481c7dff5)
点击进入onfresh()方法可以看到,onfresh()方法最终会调用ServletWebServerApplicationContext类的createWebServer()方法。在该方法中程序会进行ServletWebServerFactory对象的获取。在ServletWebServerFactory对象初始化完成后,程序就会进行DispatcherServlet的自动配置,也就是在ServletWebServerApplicationContext类的177行会完成ServletWebServerFactory对象的自动配置。
如果没有发生异常,在ServletWebServerFactory对象配置完成后会触发DispatcherServletAutoConfiguration类进行自动配置工作。也就是在这个时间点自动配置类会向IOC容器注入两个DispatcherServlet相关的Bean,createWebServer()方法的源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/115-2.jpg?sign=1739379102-b5eAEN7I7GwYEp7VgWds9GeFLqJa3lbL-0-551f30dd69a895c99b256ff0c1825e13)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/116-1.jpg?sign=1739379102-r8VSgShSnup1jqOHmLRY0pEyFGX0idSj-0-539de654fccaf076dc84e92d607b0cff)
获取ServletWebServerFactory对象是为了获取WebServer对象(在本次实例中WebServer对象为Tomcat)。接下来就是创建内嵌的Tomcat实例并进行配置,在配置完成后服务器就启动了。这些步骤在178行的getWebServer()方法中可以查看。在启动内嵌的Tomcat服务器成功后程序才可以装载DispatcherServlet。
6.4.2 创建并启动嵌入式的Tomcat对象
DispatchServlet在上一个步骤中已经完成了注册。此时,在IOC容器中已经含有名称为“dispatcherServlet”的Bean。不过此时DispatcherServlet并没有生效,只是完成了在IOC容器中的注册。而Servlet的运行环境在Servlet容器中,如果没有Servlet容器或者说Servlet容器没有启动的话,Servlet是没有任何作用的。因此还需要一个创建并启动嵌入式的Tomcat对象的流程。
查看createWebServer()方法,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/116-2.jpg?sign=1739379102-PyNhGEjVcDoclA9nxH41ooKy9qyOiooA-0-b2a563b772d5330690605a47dcc398e1)
通过源码可以发现,在ServletWebServerApplicationContext类的第177行可以获取嵌入式的Servlet容器创建工厂对象ServletWebServerFactory,在创建成功之后调用getWebServer()方法再创建Servlet容器对象。本案例中所创建的容器为Tomcat。
getWebServer()方法的作用是创建并启动Tomcat Server。最终调用的实例方法是TomcatServletWebServerFactory类的getWebServer()方法,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/117-2.jpg?sign=1739379102-2ORhhzJvTdJgm9rwvO4Hb32HH7jSiInf-0-10a2d8df426bd7b42287cf5580605e56)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-1.jpg?sign=1739379102-ThDVVjdslEyDX0fLvH1vDyJmtzLxVQyH-0-7576415cb9b9719e5ffea6d66852d9f0)
在getTomcatWebServer()方法中,调用了TomcatWebServer类的构造方法,TomcatWebServer类源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-2.jpg?sign=1739379102-SxXUrpWdoPnKXSHSOnXiplbwhtRldOmw-0-d0ca72bf7edf93faaf8573ddc3db14f8)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/119-1.jpg?sign=1739379102-1VBVegf02ZPVlb1rE2ysW588c7LeIMbI-0-f5f31105a49327c11c90b8f3c8401de9)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/120-1.jpg?sign=1739379102-Pw5zfZpv9nmJ4GIG5diwxnPnHi5BR3bK-0-b324c44f5ab6db616ea656b15995ece3)
在TomcatWebServer构造方法中最后执行了initialize()方法,在该方法中启动了嵌入式的Tomcat服务器。
在Spring Boot项目中嵌入式的Tomcat与平时使用的Tomcat在核心组件上是一样的,都包含Service、Connector、Engine、Host、Context,只是嵌入式的Tomcat服务器其初始化和启动流程都由Spring Boot来完成,开发人员无须操作。
Tomcat的启动过程分为初始化和启动两个步骤,这一小节都已经介绍完毕,接下来介绍装载DispatcherServlet的步骤。
6.4.3 装载至Servlet容器
ServletWebServerApplicationContext类第178行执行了getWebServer()方法,创建并启动了Tomcat,代码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-1.jpg?sign=1739379102-ABplyorVcLiCNYsbxgmJaYgzeq26jOGv-0-73d773d0b6a61bc692db221763746985)
重点来看一下该方法的传参:getSelfInitializer(),点击查看该方法的实现,如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-2.jpg?sign=1739379102-jA7Fp5fg3ME3nQ05am3c6KeWMABSIdsF-0-02fc060e8b30757cf627e3562717c716)
由源码可知,该方法直接返回了一个lambda表达式作为getWebServer()方法的传参。为什么可以这样呢?点开查看getWebServer()方法的定义:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-3.jpg?sign=1739379102-xmVWG2fWm8KY4rjao6UOCIoExHvnmQR5-0-607d92dd1be8723f8e762a3d56ce32a4)
getWebServer()方法的参数被定义为ServletContextInitializer类型的可变参数。ServletContextInitializer接口在定义中标注了@FunctionalInterface注解,是一个函数式接口。因此它可以直接将lambda表达式作为传参给getWebServer()方法。而selfInitialize()方法暂时不会被执行,而是在Tomcat服务器启动后被回调。selfInitialize()方法的代码定义在ServletWebServerApplicationContext类的第225行,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-4.jpg?sign=1739379102-wzWBqPoKKNeGXdbpjgESQAygzVwlZfVq-0-44595bb42bc7b1fb0c977e13d42d6d7e)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/122-1.jpg?sign=1739379102-tAP6W9CBZNfpv1x2EPGnvEYgq4ufmcRR-0-d3ffcb4d5b1d024dd90ff6fc7539d6cb)
首先来了解一下Servlet 3.0的规范。Servlet 3.0提供了可以动态注册Servlet、Filter、Listener的ServletContext相关API,开发人员可以将web.xml相关配置通过编码的方式实现,并由javax.servlet.ServletContainerInitializer的实现类负责在Servlet容器启动后进行加载。Spring提供了一个实现类org.springframework.web.SpringServletContainerInitializer,该类会调用所有实现类的onStartup()方法将相关的组件装载到Servlet容器中。
selfInitialize()会调用getServletContextInitializerBeans()方法获取所有ServletContextInitializer接口的实现类,DispatcherServletRegistrationBean就是该特殊接口的实现类。在DispatcherServletAutoConfiguration执行过程中就已经在IOC容器中注册了名称为“dispatcherServletRegistrationBean”的Bean,它会在执行getServletContext InitializerBeans()方法时被获取,最后由执行它的onStartup()方法来装载DispatcherServlet至Tomcat服务器中。
装载Servlet具体方法的调用链路如下所示。
(1)onStartup()方法的实现在RegistrationBean类中,该方法调用了register()方法注册和配置Bean,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/122-2.jpg?sign=1739379102-ee8vR4AiOYs31Ew8VZo08K88Ksu93n73-0-b177bd0a106f7db4d586488c2fed320d)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-1.jpg?sign=1739379102-r413tnGXuNseIk9slcIbNJtbtfADtL05-0-04e4256069e73c947bb4dbafb1612d37)
(2)register()方法的实现在DynamicRegistrationBean类中,该方法被执行时会调用addRegistration()方法。这里所讲的DispatcherServlet就是在该方法内进行装载的,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-2.jpg?sign=1739379102-m6ok4SISY39anOLLo5NobzcEkiIuBqRu-0-c5635b14a5f6c9cb814cc9fc3c2ab1b6)
(3)addRegistration()方法的实现在ServletRegistrationBean类中,该方法被执行时会调用addServlet()方法装载Servlet,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-3.jpg?sign=1739379102-3FwoNQq9J67rIAj1puqzMnBM4cqCQJno-0-7b6a87f706c2fa34888bc2ed8091d7a6)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-1.jpg?sign=1739379102-zo2Br12rQrnxBm5SKKoqx8Oc5oB3In7n-0-c16db958c65023340bde8390b2bfc326)
(5)addServlet()方法具体实现如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-2.jpg?sign=1739379102-iprb16NPaxRzX71uGw6S6kLDB19cEf1Q-0-f3a5258c57ec563a9e6783f155b3b951)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/125-1.jpg?sign=1739379102-C0XTcLvGxURYaoAQt4yq0fGq8APIocoS-0-7db4e26fc9c464085bae964e36dd7088)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/126-1.jpg?sign=1739379102-sb8moMA2irZoltwAkcWn7fTlixLYml7D-0-5326acea6cba4bfaf0bc908ba653869e)
最终,通过onStartup()方法将DispatchServlet装载到Servlet容器中(即Tomcat服务器),在Tomcat服务器启动后就能够使用DispatchServlet进行请求映射和拦截处理了。
综上所述,由于Spring Boot的自动配置,开发人员在Spring Boot项目中引入spring-boot-starter-web场景启动器之后无须进行任何设置也可以进行Web开发。
本章的主要内容就是介绍Spring Boot中DispatcherServlet自动配置的全部流程和知识点,主要围绕以下三个问题展开了讨论。
(1)DispatcherServletAutoConfiguration自动配置类做了哪些事?
主要是向IOC容器中注册了两个Bean,名称分别为“DispatcherServlet”和“DispatcherServletRegistration”,即org.springframework.web.servlet.DispatcherServlet和org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean。
(2)DispatcherServletAutoConfiguration自动配置类是何时执行的?
结合Spring Boot项目启动过程可以得出,自动配置类的执行是在ServletWebServer Factory对象获取之后触发的,方法调用链如图6-16所示。
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/126-2.jpg?sign=1739379102-gyNkpguAumm3sMDXQfe7bLDTkJSIWsS8-0-57b473b251cef563d34286c032cb314f)
图6-16 Spring Boot项目启动方法调用链
(3)DispatcherServlet是如何被装载到Servlet容器中并生效的?
Servlet容器在启动之后会回调selfInitialize()方法,在该方法完成了DispatcherServlet的装载过程,该方法调用链如图6-17所示。
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/127-1.jpg?sign=1739379102-t7bqryAM3thlTZaHESUiiWbtJ9lSy72I-0-eb3b9200ac391342a286a10ddc9cd130)
图6-17 DispatcherServlet装载方法的调用链
以上所涉及的类和方法读者可以按照书中的提示,自行查看Spring Boot的源码并手动调试,这样才能更好地理解整个DispatcherServlet自动配置和装载的过程。
另外,文章中涉及的源码都来自Spring Boot 2.3.7-RELEASE版本,与其他版本的代码可能些许不同。如果是Spring Boot 2.0之前的版本差异会更大,这一点需要注意。