![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=1739571016-QAqlHBgPio9fBNsWIK0zifYuMkG4j1Hk-0-e338fb5e20b2bc2fae8c51c8974bf718)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/115-1.jpg?sign=1739571016-OnaRtDyYsI40c25br6rTAPEKjReV9rqj-0-2eb6cc6301242e373b25ccec07f77244)
点击进入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=1739571016-RslLdyLpLDbfNtjFGZjLrYaHj5kwubR2-0-b07e8b688b58ad8758143bc92cc74320)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/116-1.jpg?sign=1739571016-ToBsO68jOfzMR23348IshLFi31uEz0hY-0-d706fb74fb6d24af79ceef22a0d0a04e)
获取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=1739571016-e9n2yF1TAheDfXE3KJ7sRJLuBdvOj8AT-0-7c99f25d064690e8d9f9033267856977)
通过源码可以发现,在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=1739571016-awG7kYA3v2Vf1ArL2EMghAYufyrPnWWd-0-2eac878e8ae6dad7caa9d603e752fb23)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-1.jpg?sign=1739571016-7XqHvs0YHyUyJ7SOpCwIS26q1kZJXTLI-0-a599f7210a1d79df1ad809742382f22e)
在getTomcatWebServer()方法中,调用了TomcatWebServer类的构造方法,TomcatWebServer类源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/118-2.jpg?sign=1739571016-ISR6BXGLFZnvzFbyEpNXOyR62jbdst2I-0-e246f14c0c1375c2d758e74e0de1835b)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/119-1.jpg?sign=1739571016-RIImQCx38rMoVLAgYRYewrRkyy29IRZa-0-132fe02c431a10a46efe79760d5732f6)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/120-1.jpg?sign=1739571016-OxA7zwneGBXGd5ym1bI3u9zxAazNd5Ud-0-f9e8c019b2ea041557e8c15fab37ec51)
在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=1739571016-3XSEPY2YpDmNAfxin9DydF2AOR3oUCyF-0-360d1fa479019d87bb3e00f72f4ef437)
重点来看一下该方法的传参:getSelfInitializer(),点击查看该方法的实现,如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-2.jpg?sign=1739571016-6uXeA3uXzdebFgwL0eiHIxtakjT1FH9C-0-38c76179438a184754e8b2f329814da2)
由源码可知,该方法直接返回了一个lambda表达式作为getWebServer()方法的传参。为什么可以这样呢?点开查看getWebServer()方法的定义:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-3.jpg?sign=1739571016-YmCEIpE7utIGirzfiawe8I67yaWRLrDG-0-189cbf195d017814c7f436b8c7e6a067)
getWebServer()方法的参数被定义为ServletContextInitializer类型的可变参数。ServletContextInitializer接口在定义中标注了@FunctionalInterface注解,是一个函数式接口。因此它可以直接将lambda表达式作为传参给getWebServer()方法。而selfInitialize()方法暂时不会被执行,而是在Tomcat服务器启动后被回调。selfInitialize()方法的代码定义在ServletWebServerApplicationContext类的第225行,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/121-4.jpg?sign=1739571016-FSFfbNMKgLzobRVy3ErsIT95j2QSsFiv-0-14a688923eb7014826515961451d6c34)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/122-1.jpg?sign=1739571016-AspPpt5M4fvp3uefBXkFGT7Q7J9qgOdw-0-387dee3f51fd9d075a1d9f7c278000a7)
首先来了解一下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=1739571016-5jAXZwhNXgCN4GBxekZXbgKWeXzu5WxA-0-40c38cf7d86a467498fb3eb9894b61fa)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-1.jpg?sign=1739571016-ZEGm1raIsaHF9UKgNoATyYm1LIIhnN1N-0-9fa6da5b43886aacd7a58be3b0d7366f)
(2)register()方法的实现在DynamicRegistrationBean类中,该方法被执行时会调用addRegistration()方法。这里所讲的DispatcherServlet就是在该方法内进行装载的,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-2.jpg?sign=1739571016-EFbcc5pSkxZ08aI45FvH7k1DApXXMOyr-0-b6fbfabf16c9c5a726acbc3a3171aabb)
(3)addRegistration()方法的实现在ServletRegistrationBean类中,该方法被执行时会调用addServlet()方法装载Servlet,源码如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/123-3.jpg?sign=1739571016-qxZWE75vyIdi2fz9GZTVAJlp2fFCAoke-0-f2df9f0369b05b691ae50cf78da010bc)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-1.jpg?sign=1739571016-sOnXBXBkiK3F07u54Cf9pFqpOpbOZUEw-0-f3ffd6eee5e3185041659a554af41640)
(5)addServlet()方法具体实现如下所示:
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/124-2.jpg?sign=1739571016-StZRg3xa9I9SSbdbPWYzcd08MnwpJojV-0-be3b8b0fdb46d531dfbaf0c599d93e62)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/125-1.jpg?sign=1739571016-3nFs2f4VdFvOANUcmgvd7vzdaOGkT0rm-0-e25409f8146e56caed8a711c17c6febf)
![](https://epubservercos.yuewen.com/3069E4/20862583308964806/epubprivate/OEBPS/Images/126-1.jpg?sign=1739571016-2hdqHqlGGThzslgwsu1mbbb6iw7WxBAL-0-cdb4df0b3cca53c20ed11563a3be892b)
最终,通过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=1739571016-xfyjqPeOVARRdsjrcNU7qq8dua6tRppi-0-fa5818b7f2830d29160b2caa54c9777a)
图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=1739571016-8d8UuZWkLzRuldhuhySdCfn75Jepocry-0-a0d71543a158121e965a8345a5d3dad7)
图6-17 DispatcherServlet装载方法的调用链
以上所涉及的类和方法读者可以按照书中的提示,自行查看Spring Boot的源码并手动调试,这样才能更好地理解整个DispatcherServlet自动配置和装载的过程。
另外,文章中涉及的源码都来自Spring Boot 2.3.7-RELEASE版本,与其他版本的代码可能些许不同。如果是Spring Boot 2.0之前的版本差异会更大,这一点需要注意。