SpringBoot集成Tomcat、DispatcherServlet

通过 SpringBoot 自动配置机制,导入配置类

利用 SpringBoot 自动配置机制,SpringBoot 会导入一个类型为 ServletWebServerFactoryAutoConfiguration 的配置类

ServletWebServerFactoryAutoConfiguration

ServletWebServerFactoryAutoConfigurations 类上存在 @Import 注解,@Import 注解有以下几个特性:

  • 继承 ImportSelector 接口 :会在解析阶段执行 selectImports 方法,方法返回的类名数组,会被解析成 BeanDefinition 对象,即后期会被实例化成Bean
  • 继承 ImportBeanDefinitionRegistrar 接口 :会在解析阶段执行 registerBeanDefinitions 方法,一般会注册 BeanDefinition 对象
  • 配置类

ServletWebServerFactoryAutoConfiguration 类属于第三种情况,我们继续分析

ServletWebServerFactoryConfiguration

spring-boot-starter-web 默认包含依赖 spring-boot-starter-tomcat,因此 Spring 中存在类型为 EmbeddedTomcat 的配置类,EmbeddedJetty、EmbeddedUndertow 因为不满足 @ConditionalOnClass 注解的条件,所以默认情况下 web 容器是 Tomcat

如何修改默认 Web 容器
复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.6.13</version>
    <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <version>2.6.13</version>
</dependency>

此时项目是以 Jetty 为 Web 容器

SpringBoot集成Tomcat

AbstractApplicationContext#refresh

AbstractApplicationContext 的 refresh 方法是一个空壳方法,我们主要看它的实现类 ServletWebServerApplicationContext

ServletWebServerApplicationContext#refresh

getSelfInitializer

其中 getSelfInitializer 方法的返回类型是 ServletContextInitializer, ServletContextInitializer 是一个函数式接口

所以上述代码等价于下列形式:

复制代码
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return new ServletContextInitializer() {
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            prepareWebApplicationContext(servletContext);
            registerApplicationScope(servletContext);
            WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
            for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                beans.onStartup(servletContext);
            }
        }
    };
}
getWebServer
Tomcat#start

经过一系列的方法调用,最终会调用 StandardContext 的 startInternal 方法

TomcatStarter#onStartup

其中 ServletWebServerApplicationContext$lambda 就是我们上文分析的 getSelfInitializer 方法

ServletContextInitializer#onStartup

ServletContextInitializer 的 onStartup 方法,会向容器中注册 Servlet、Filter,DispatcherServlet 就是在这个阶段注册进去的

SpringBoot集成DispatcherServlet

我们在上文中已经分析过了,默认情况下 Spring 中存在的与 web 容器相关的配置类是 EmbeddedTomcat ,该配置类会定义一个类型为 TomcatServletWebServerFactory 的 bean

通过 TomcatServletWebServerFactory 的类图,我们可以知道它是 ErrorPageRegistry 的子类

ErrorPageRegistrarBeanPostProcessor#postProcessBeforeInitialization

其中 getRegistrars 方法会查找 Spring 中类型为 ErrorPageRegistrar 的 bean,在自动配置类 ErrorMvcAutoConfiguration 中就定义了一个类型为 ErrorPageCustomizer 的 bean,该 bean 是 ErrorPageRegistrar 的子类

ErrorPageCustomizer的实例化

errorPageCustomizer 方法会有一个类型为 DispatcherServletPath,根据 Spring 的机制,它会从Spring 中先查找类型为 DispatcherServletPath 的 bean,如果该 bean 还没有被实例化,则优先实例化 bean,作用类似于 @DependsOn

DispatcherServletRegistrationConfiguration#dispatcherServletRegistration

我们再追踪 DispatcherServletPath 的实例化过程,DispatcherServletPath 是一个函数式接口,它有一个实现类 DispatcherServletRegistrationBean

DispatcherServletAutoConfiguration 的内部类 DispatcherServletRegistrationConfiguration 定义了一个返回类型为 DispatcherServletRegistrationBean 的 bean。这个方法有一个类型为 DispatcherServlet 的参数,和 DispatcherServletPath 的特性一致,它会优先实例化类型为 DispatcherServlet 的 bean

DispatcherServletConfiguration#dispatcherServlet

此时 Spring 中就有一个类型为 DispatcherServlet 的 bean

DispatcherServlet#onRefresh

如果 DispatcherServlet 还没有执行 onRefresh 方法,第一次发送请求,就会执行 onRefresh 方法

FrameworkServlet#onRefresh
DispatcherServlet#onRefresh

至此 DispatcherServlet 的实例化以及组件初始化已经完成

相关推荐
前端小马16 小时前
前后端Long类型ID精度丢失问题
java·前端·javascript·后端
Lisonseekpan16 小时前
Java Caffeine 高性能缓存库详解与使用案例
java·后端·spring·缓存
柳贯一(逆流河版)16 小时前
Spring Boot Actuator+Micrometer:高并发下 JVM 监控体系的轻量化实践
jvm·spring boot·后端
SXJR17 小时前
Spring前置准备(七)——DefaultListableBeanFactory
java·spring boot·后端·spring·源码·spring源码·java开发
java水泥工17 小时前
酒店客房管理系统|基于SpringBoot和Vue的酒店客房管理系统(源码+数据库+文档)
spring boot·vue·酒店管理系统·酒店客房管理系统
Moonbit17 小时前
MoonBit高校行 | 中大、深技大、深大、港科广回顾
后端·开源·编程语言
纸照片17 小时前
【邪修玩法】如何在WPF中开放 RESTful API 服务
后端·wpf·restful
心态特好18 小时前
详解WebSocket及其妙用
java·python·websocket·网络协议
Terio_my19 小时前
Spring Boot 虚拟 MVC 调用
spring boot
Haooog19 小时前
98.验证二叉搜索树(二叉树算法题)
java·数据结构·算法·leetcode·二叉树