侧边栏壁纸
博主头像
GG's Blog博主等级

行动起来,活在当下

  • 累计撰写 23 篇文章
  • 累计创建 12 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

SpringBoot如何内嵌的Tomcat

mrqinzh
2024-06-10 / 0 评论 / 0 点赞 / 26 阅读 / 6915 字

对于一个 SpringBoot web 工程来说,一个主要的依赖就是有 spring-boot-starter-web 这个 starter ,spring-boot-starter-web 模块在 spring boot 中并没有代码存在,只是在 pom.xml 中携带了一些其他的web相关的依赖,包括 webmvc、tomcat 等等.这些 提供了一个 web 应用的运行环境。

在 spring-boot-autoconfigure 模块中,有处理关于 WebServer 的自动配置类 ServletWebServerFactoryAutoConfiguration

代码片段如下:

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration

两个 Condition 表示当前运行环境是基于 servlet 标准规范的 web 服务:

  • ConditionalOnClass(ServletRequest.class) : 表示当前必须有 servlet-api 依赖存在

  • ConditionalOnWebApplication(type = Type.SERVLET) :仅基于servlet的Web应用程序

@EnableConfigurationProperties(ServerProperties.class):ServerProperties 配置中包括了常见的 server.port 等配置属性。

通过 @Import 导入嵌入式容器相关的自动配置类,有 EmbeddedTomcat、EmbeddedJetty 和EmbeddedUndertow。

综合来看,ServletWebServerFactoryAutoConfiguration 自动配置类中主要做了以下几件事情:

  • 导入了内部类 BeanPostProcessorsRegistrar,它实现了 ImportBeanDefinitionRegistrar,可以实现ImportBeanDefinitionRegistrar 来注册额外的 BeanDefinition。

  • 导入了 ServletWebServerFactoryConfiguration.EmbeddedTomcat 等嵌入容器相关配置 EmbeddedTomcat 就是tomcat的相关配置

  • 注册了ServletWebServerFactoryCustomizer、TomcatServletWebServerFactoryCustomizer 两个WebServerFactoryCustomizer 类型的 bean。

我们可以关注一下这两个 Customizer Bean, 这两个 Customizer 实际上就是去处理一些配置值,然后绑定到 各自的工厂类的。

  • WebServerFactoryCustomizer 将 serverProperties 配置值绑定给 ConfigurableServletWebServerFactory 对象实例上

  • TomcatServletWebServerFactoryCustomizer  主要处理 Tomcat 相关的配置值

整体结构如下图

TomcatServletWebServerFactory 是用于获取 Tomcat 作为 WebServer 的工厂类实现,其中最核心的方法就是 getWebServer,获取一个 WebServer 对象实例

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 创建一个 Tomcat 实例
    Tomcat tomcat = new Tomcat();
    // 创建一个 Tomcat 实例工作空间目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory
        : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // 创建连接对象
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    // 1
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    // 配置 Engine,没有什么实质性的操作,可忽略
    configureEngine(tomcat.getEngine());
    // 一些附加链接,默认是 0 个
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    // 2
    prepareContext(tomcat.getHost(), initializers);
    // 返回 webServer
    return getTomcatWebServer(tomcat);
}
  • customizeConnector : 给 Connector 设置 port、protocolHandler、uriEncoding 等。Connector 构造的逻辑主要是在NIO和APR选择中选择一个协议,然后反射创建实例并强转为 ProtocolHandler

  • 2、prepareContext 这里并不是说准备当前 Tomcat 运行环境的上下文信息,而是准备一个 StandardContext ,也就是准备一个 web app。

对于 Tomcat 来说,每个 context 就是映射到 一个 web app 的,所以 prepareContext 做的事情就是将 web 应用映射到一个 TomcatEmbeddedContext ,然后加入到 Host 中

简单来说 prepareContext 就是在设置类加载器 WebappLoader 可以通过 setLoaderClass 和 getLoaderClass 这两个方法可以更改loaderClass 的值。所以也就意味着,我们可以自己定义一个继承 webappClassLoader 的类,来更换系统自带的默认实现。我们自定义的servlet就是这样被tomcat加载到的。

后续就是tomcat本身的过程了

上面对 SpringBoot 中内嵌 Tomcat 的过程做了分析,这个过程实际上并不复杂,就是在刷新 Spring 上下文的过程中将 Tomcat 容器启动起来,并且将当前应用绑定到一个 Context ,然后添加了 Host

总结一下整个过程:

  • 通过自定配置注册相关的 Bean ,包括一些 Factory 和 后置处理器等

  • 上下文刷新阶段,执行创建 WebServer,这里需要用到前一个阶段所注册的 Bean

    • 包括创建 ServletContext

    • 实例化 webServer

  • 创建 Tomcat 实例、创建 Connector 连接器

  • 绑定 应用到 ServletContext,并添加相关的生命周期范畴内的监听器,然后将 Context 添加到 host 中

  • 实例化 webServer 并且启动 Tomcat 服务


0

评论区