![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=1739597845-nTmNgSKF3i0uGQZg2ArzFDD8XhLar3u7-0-c559cb6fbe489870fd8d367da254ad9c)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/115-1.jpg?sign=1739597845-fJ52NquyzzGKlwkXBe1Hq5z0wndjVLiZ-0-512117da1e645e3a618396dc29f91395)
点击进入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=1739597845-M5g4tlRCJ5h2wF0Gbm7aaKTE4Ll0YEYu-0-120b22809108f6638789eb270df2f5cd)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/116-1.jpg?sign=1739597845-TbNhzzRqFWoV1jnYe4wfPbaBdgUsrmEA-0-58e9e08717875200dc632a5f1766e855)
获取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=1739597845-SJVhOQ7mEnYHHe76pUZMEKgwx8vUCgCp-0-cc7ec3ba135510b6b74091ff9b4c9bbb)
通过源码可以发现,在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=1739597845-K2h89wlRhvOrUzdTAapYeXbdkymRwytz-0-80e1b2458720cd7af2c2da82ff5c65db)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-1.jpg?sign=1739597845-U7xnGMdV2mHuRngL0n3S6QCMZePCe5fa-0-d9c5c0fca5caeae13b66382761ed655c)
在getTomcatWebServer()方法中,调用了TomcatWebServer类的构造方法,TomcatWebServer类源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-2.jpg?sign=1739597845-HlpSnpSB5G1bKkg6Hymc1iqMLUCMMFa3-0-cfb7a0055b383fd1e6addad3e3272fa6)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/119-1.jpg?sign=1739597845-tKPs43smllAqdN11w16XSC4GmU7M3CPP-0-6c2a76f5db7e03867dc53bd44ac978ac)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/120-1.jpg?sign=1739597845-AXcSax56Emui8Paa1NI9wWMJlRL5wTNM-0-fd4fcaea9b2e6a4b8b3833ee7b461fc2)
在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=1739597845-EPedcpLx4wWVBng12p1cUAEJDlbQjibt-0-70cf192bf8a2802fe8775b3b82e7bbf9)
重点来看一下该方法的传参:getSelfInitializer(),点击查看该方法的实现,如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-2.jpg?sign=1739597845-h77Ih5OQV1v8HjzwAgKiYuGB0HIA8gFq-0-9ffc3b4064055a3b0cc2b4aa4ec51fb4)
由源码可知,该方法直接返回了一个lambda表达式作为getWebServer()方法的传参。为什么可以这样呢?点开查看getWebServer()方法的定义:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-3.jpg?sign=1739597845-MZSw54ftTXl8jnMAhVnVtwNRKD0T8TdK-0-6d9e5c058b6cb7c7a84bfff1f94209cd)
getWebServer()方法的参数被定义为ServletContextInitializer类型的可变参数。ServletContextInitializer接口在定义中标注了@FunctionalInterface注解,是一个函数式接口。因此它可以直接将lambda表达式作为传参给getWebServer()方法。而selfInitialize()方法暂时不会被执行,而是在Tomcat服务器启动后被回调。selfInitialize()方法的代码定义在ServletWebServerApplicationContext类的第225行,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-4.jpg?sign=1739597845-Rk8QAfyzV0kaKyIzhsOspVDtLrenez3z-0-37659bb4ed311039fc786af2000477a3)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/122-1.jpg?sign=1739597845-iJdPX6onsost18uNFi7QYdrl964LEKza-0-37ec78d4de42e2a6d03d0bc282f7b961)
首先来了解一下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=1739597845-H0SA9lSsenyGvypkfIzgiIke5M6AOL97-0-ad4c68127c0f389ec75313d0603db0f0)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-1.jpg?sign=1739597845-5DvZ0R99UJhfVyP9RbrdaESjFR9K1fCW-0-ef01873e09f797e3d6a945a8f152f80c)
(2)register()方法的实现在DynamicRegistrationBean类中,该方法被执行时会调用addRegistration()方法。这里所讲的DispatcherServlet就是在该方法内进行装载的,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-2.jpg?sign=1739597845-0C1LLbFExownLSzn4tVXH9Va3daL28ob-0-770a0ca1045e661a1df7d0e13b11d82b)
(3)addRegistration()方法的实现在ServletRegistrationBean类中,该方法被执行时会调用addServlet()方法装载Servlet,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-3.jpg?sign=1739597845-SDlBbktdXcgPH6s6NJIWebvDIkXYHoFZ-0-c9794c008c3f03b90967d7134c0c9e46)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-1.jpg?sign=1739597845-SxXnywIWMssIypYUGhjkI8D8orP8VZR3-0-a0778af1a3602e3a438574ea6c2cd294)
(5)addServlet()方法具体实现如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-2.jpg?sign=1739597845-kl11VIGFdXWwkc62bv63tTwZKSz1VIgA-0-8723e1796c267b4657e511c84ddd213f)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/125-1.jpg?sign=1739597845-erwaIBSi00o7gk4D6G407KD2Job1Nm72-0-c6662742c2ecf0c47d336e57f47dcf0d)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/126-1.jpg?sign=1739597845-EWW2JTes36IYPivZBIbhOWbI2WWLUbEw-0-b43e45c4211f88c70cb15b677062d32f)
最终,通过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=1739597845-9Fw3mpwNJZEoQNTlJsMA8VsR7szMZSI1-0-7131d18c4a65dbc66ef93d9ee0e0f2ed)
图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=1739597845-xcL12nZr9QI2zpq23mBBbhnNdwkYFm7G-0-5414d6b9f6081a9b61a2a1b8282e2350)
图6-17 DispatcherServlet装载方法的调用链
以上所涉及的类和方法读者可以按照书中的提示,自行查看Spring Boot的源码并手动调试,这样才能更好地理解整个DispatcherServlet自动配置和装载的过程。
另外,文章中涉及的源码都来自Spring Boot 2.3.7-RELEASE版本,与其他版本的代码可能些许不同。如果是Spring Boot 2.0之前的版本差异会更大,这一点需要注意。