![Spring Boot实战:从0开始动手搭建企业级项目](https://wfqqreader-1252317822.image.myqcloud.com/cover/850/40107850/b_40107850.jpg)
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=1739642462-3duD6ySRp8JszzegpRM3hua2QprWzlYc-0-04c92f6e032d80d599372def51e20842)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/115-1.jpg?sign=1739642462-LZe2ZDBsCNgfjHflbESah86yQY9CqSzY-0-21310723c1341da26288638f6bba9d60)
点击进入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=1739642462-YiDjSNaKEiz2LtaEzxgbGhOuTCrVe5b0-0-8d0d77e69e076a0a3721e08b9e710d25)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/116-1.jpg?sign=1739642462-CgLYQzWFSEiatqJ9YU441SXOjAELTAt6-0-a907b359f52f5ebae7dd1a38bdd36a61)
获取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=1739642462-jqKBXjbb0C53y9eSuOPN9c14WG7bJccY-0-888c15e66ea9c12ccc2b47905c570207)
通过源码可以发现,在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=1739642462-iOSe9yTPDkM4D5R7AhxhdPXxFFW7BNq9-0-e44bed8548a0595edd2a8eb73fb69728)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-1.jpg?sign=1739642462-pIGyZ0aSmWjhE9YCl1ECWncnxPakAqeu-0-6b4a9b66dc3892ac26aec6360435d2a5)
在getTomcatWebServer()方法中,调用了TomcatWebServer类的构造方法,TomcatWebServer类源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-2.jpg?sign=1739642462-dxC7O0H372SEltX1bViA65ZrJFB5aNqI-0-ac96a0ab835b820c614e6ec5bfd67537)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/119-1.jpg?sign=1739642462-yFceB7urRQd39cK7TiKmKBoMSBcigZUf-0-726e2cf62b21f64e8e3e84fd025778e1)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/120-1.jpg?sign=1739642462-50TyMhw1jXV3NS4q4UySdV7Lp7caJaQx-0-28762cf61344c8ec6fdc73f6d83259e2)
在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=1739642462-IcSJl5eZINTBNLQ86rEbi6ACBQCE2Bx3-0-d7ced84d63691d42324f78dbf7fb7ffe)
重点来看一下该方法的传参:getSelfInitializer(),点击查看该方法的实现,如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-2.jpg?sign=1739642462-1nmTkzPigmmoLGZDrAPGbZ7mDEsP6GGU-0-97974ba942811201ae4a39c1ed5e7d99)
由源码可知,该方法直接返回了一个lambda表达式作为getWebServer()方法的传参。为什么可以这样呢?点开查看getWebServer()方法的定义:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-3.jpg?sign=1739642462-eL2aCEY9VEuqr7v0A84UjalCZkwytsOA-0-6108f0502d1785e2b38dc96d549773d9)
getWebServer()方法的参数被定义为ServletContextInitializer类型的可变参数。ServletContextInitializer接口在定义中标注了@FunctionalInterface注解,是一个函数式接口。因此它可以直接将lambda表达式作为传参给getWebServer()方法。而selfInitialize()方法暂时不会被执行,而是在Tomcat服务器启动后被回调。selfInitialize()方法的代码定义在ServletWebServerApplicationContext类的第225行,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-4.jpg?sign=1739642462-ajV8q8JpMcu5Wxx5CoOaMcmz2osQQ8bC-0-bd1cf2a85ab8a41476523fb7015f6a6b)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/122-1.jpg?sign=1739642462-CsvXK5cYO0tYOsfzGoxwT1oi7yDhfDoz-0-f8623e2857aa1627044207edefc7ca5e)
首先来了解一下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=1739642462-0EfELNRLTG8asA6mPc7rIrmbvTu1yoto-0-3bc14bafbb073abd9ff5b49e4816ddda)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-1.jpg?sign=1739642462-bKu59zJ0t217tFlSieLR2gwNgsCHnuHZ-0-1f2b309b772968d3b0674e4a1da8946b)
(2)register()方法的实现在DynamicRegistrationBean类中,该方法被执行时会调用addRegistration()方法。这里所讲的DispatcherServlet就是在该方法内进行装载的,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-2.jpg?sign=1739642462-enfrxorBZW99UWUH8aKQgpYRJp2ldOEO-0-193645049539c3ea931ac4201cd6a4cf)
(3)addRegistration()方法的实现在ServletRegistrationBean类中,该方法被执行时会调用addServlet()方法装载Servlet,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-3.jpg?sign=1739642462-mCnGwM45ZYPz4S76OXGxeWarUGw7yYJh-0-eb649fe9c7edaf024d4154f79cdd5374)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-1.jpg?sign=1739642462-Dg9ywtDJbog0DGXzNNReqL5F7MwHKDba-0-652fe567d2368f0f9b684e9fcf69997d)
(5)addServlet()方法具体实现如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-2.jpg?sign=1739642462-uiHrKMzbvj4Xf7MQ96YgHznEECAHXReY-0-656faccec12b3b0456c08f5476976642)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/125-1.jpg?sign=1739642462-HNTbHbHLe5LH3JwQaELqzb53ojRr7TE2-0-93c5ff459b857bafcd0e1ee6253bb937)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/126-1.jpg?sign=1739642462-VjlqNWm0gyv24G5PYuwxTs9ozURtNpxp-0-c8a0c1d177d91b9588c94a591942cd10)
最终,通过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=1739642462-vVfZXuLlhlpwyaM6CNanAppyjF9ZrdOv-0-4eeab9c741e42092dc34aae1d3aa1771)
图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=1739642462-NgPaB2ss0zecyOcDg9EBAGmOfFETauml-0-ced5a4ad04a86a1c1ebfebae250552f7)
图6-17 DispatcherServlet装载方法的调用链
以上所涉及的类和方法读者可以按照书中的提示,自行查看Spring Boot的源码并手动调试,这样才能更好地理解整个DispatcherServlet自动配置和装载的过程。
另外,文章中涉及的源码都来自Spring Boot 2.3.7-RELEASE版本,与其他版本的代码可能些许不同。如果是Spring Boot 2.0之前的版本差异会更大,这一点需要注意。