服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

云服务器|WEB服务器|FTP服务器|邮件服务器|虚拟主机|服务器安全|DNS服务器|服务器知识|Nginx|IIS|Tomcat|

服务器之家 - 服务器技术 - Tomcat - Spring boot整合tomcat底层原理剖析

Spring boot整合tomcat底层原理剖析

2022-10-17 16:57zfcq Tomcat

SpringBoot的启动过程中,会调用核心的refresh方法,内部会执行onRefresh()方法,onRefresh()方法是一个模板方法,他会执行会执行子类ServletWebServerApplicationContext的onRefresh()方法,这篇文章主要介绍了Spring boot整合tomcat底层原理,需要的朋友

从源码层面理解spring boot的默认web容器,以及他们是如何关联起来的。

本文结论

  • 源码基于spring boot2.6.6
  • 项目的pom.xml中存在spring-boot-starter-web的时候,在项目启动时候就会自动启动一个Tomcat。
  • 自动配置类ServletWebServerFactoryAutoConfiguration找到系统中的所有web容器。我们以tomcat为主。
  • 构建TomcatServletWebServerFactory的bean。
  • SpringBoot的启动过程中,会调用核心的refresh方法,内部会执行onRefresh()方法,onRefresh()方法是一个模板方法,他会执行会执行子类ServletWebServerApplicationContext的onRefresh()方法。
  • onRefresh()方法中调用getWebServer启动web容器。

spring-boot-starter-web内部有什么?

  • 在spring-boot-starter-web这个starter中,其实内部间接的引入了spring-boot-starter-tomcat这个starter,这个spring-boot-starter-tomcat又引入了tomcat-embed-core依赖,所以只要我们项目中依赖了spring-boot-starter-web就相当于依赖了Tomcat。

Spring boot整合tomcat底层原理剖析

Spring boot整合tomcat底层原理剖析

自动配置类:ServletWebServerFactoryAutoConfiguration在spring-boot-autoconfigure-2.6.6.jar这个包中的spring.factories文件内,配置了大量的自动配置类,其中就包括自动配置tomcat的自动配置类:ServletWebServerFactoryAutoConfiguration

Spring boot整合tomcat底层原理剖析

自动配置类的代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// full模式
@Configuration(proxyBeanMethods = false)
 
// 配置类解析顺序
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
 
// 条件注解:表示项目依赖中要有ServletRequest类(server api)
@ConditionalOnClass(ServletRequest.class)
// 表示项目应用类型得是SpringMVC(在启动过程中获取的SpringBoot应用类型)
@ConditionalOnWebApplication(type = Type.SERVLET)
 
// 读取server下的配置文件
@EnableConfigurationProperties(ServerProperties.class)
 
// import具体的加载配置的类和具体web实现容器
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    ......
}
  • ServletRequest是存在于tomcat-embed-core-9.0.60.jar中的的一个类,所以@ConditionalOnClass(ServletRequest.clas s)会满足。
  • spring-boot-starter-web中,间接的引入了spring-web、spring-webmvc等依赖,所以@ConditionalOnWebApplication(type = Type.SERVLET)条件满足。
  • 上面的俩个条件都满足,所以spring回去解析这个配置类,在解析过程中会发现他import了三个类!我们重点关注EmbeddedTomcat。其他俩个的内部条件注解不满足!
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration(proxyBeanMethods = false)
// tomcat内部的类,肯定都存在
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
 
// 程序员如果自定义了ServletWebServerFactory的Bean,那么这个Bean就不加载。
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(
        ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
        ObjectProvider<TomcatContextCustomizer> contextCustomizers,
        ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            // orderedStream()调用时会去Spring容器中找到TomcatConnectorCustomizer类型的Bean,默认是没有的,程序员可以自己定义。这个Bean可以设置一些tomcat的配置,比如端口、协议...
            // TomcatConnectorCustomizer:是用来配置Tomcat中的Connector组件的
            factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
            // TomcatContextCustomizer:是用来配置Tomcat中的Context组件的
            factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
            // TomcatProtocolHandlerCustomizer:是用来配置Tomcat中的ProtocolHandler组件的
            factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }
}
  • 对于另外的EmbeddedJetty和EmbeddedUndertow,逻辑类似,都是判断项目依赖中是否有Jetty和Undertow的依赖,如果有,那么对应在Spring容器中就会存在JettyServletWebServerFactory类型的Bean、或者存在UndertowServletWebServerFactory类型的Bean。

TomcatServletWebServerFactory的作用:获取WebServer对象

  • TomcatServletWebServerFactory他实现了ServletWebServerFactory这个接口。
  • ServletWebServerFactory接口内部只有一个方法是获取WebServer对象。

Spring boot整合tomcat底层原理剖析

  • WebServer拥有启动、停止、获取端口等方法,就会发现WebServer其实指的就是Tomcat、Jetty、Undertow。

Spring boot整合tomcat底层原理剖析

  • 而TomcatServletWebServerFactory就是用来生成Tomcat所对应的WebServer对象,具体一点就是TomcatWebServer对象,并且在生成TomcatWebServer对象时会把Tomcat给启动起来。
  • 在源码中,调用TomcatServletWebServerFactory对象的getWebServer()方法时就会启动Tomcat。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    // 构建tomcat对象
    Tomcat tomcat = new Tomcat();
 
    // 设置相关的属性
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
 
    // 启动tomcat,这个方法内部有this.tomcat.start();
    return getTomcatWebServer(tomcat);
}

spring boot启动的时候启动tomcat

  • SpringBoot的启动过程中,会调用核心的refresh方法,内部会执行onRefresh()方法,onRefresh()方法是一个模板方法,他会执行会执行子类ServletWebServerApplicationContext的onRefresh()方法。
?
1
2
3
4
5
6
7
8
9
10
11
protected void onRefresh() {
    // 模板方法,先调用它父类的,一般是空方法
    super.onRefresh();
    try {
        // 创建web容器
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

这个方法会调用createWebServer()方法。

?
1
2
3
4
5
6
7
8
9
10
// 最核心的俩行代码
private void createWebServer() {
    ......
    // 获取web容器,多个或者没有的时候报错
    ServletWebServerFactory factory = getWebServerFactory();
 
    // 调用这个容器的getWebServer方法,上面的启动tomcat的方法!
    this.webServer = factory.getWebServer(getSelfInitializer());
    ......
}
  • getWebServerFactory控制项目组有且只能有一个web容器!
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
 
    // 得到所有类型为ServletWebServerFactory的Bean。TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory都是他得到子类!
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
 
    // 不存在,报错
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
    }
 
    // 存在不止一个,报错!
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
 
    // 返回唯一的一个web容器!
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

获取tomcat的配置

  • 自动配置类ServletWebServerFactoryAutoConfiguration上除了import三个web容器,还import了BeanPostProcessorsRegistrar。
  • BeanPostProcessorsRegistrar实现了ImportBeanDefinitionRegistrar,所以他会在spring启动的时候调用registerBeanDefinitions方法。
  • registerBeanDefinitions会注册一个Bean:webServerFactoryCustomizerBeanPostProcessor。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // Bean工厂,一个Aware回调进行赋值
    if (this.beanFactory == null) {
        return;
    }
    // 注册webServerFactoryCustomizerBeanPostProcessor这个Bean。
    registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
        WebServerFactoryCustomizerBeanPostProcessor.class,
        WebServerFactoryCustomizerBeanPostProcessor::new);
 
    // 注册errorPageRegistrarBeanPostProcessor
    registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
        ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
}
  • webServerFactoryCustomizerBeanPostProcessor实现了BeanPostProcessor,所以他会在启动的时候调用postProcessBeforeInitialization方法。
?
1
2
3
4
5
6
7
8
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
    // 找到WebServerFactoryCustomizer的Bean
    LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
        // 标记日志用的类
        .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
        // 调用customize方法,传入webServerFactory
        .invoke((customizer) -> customizer.customize(webServerFactory));
}
  • postProcessBeforeInitialization中会调用WebServerFactoryCustomizer类customize方法,在系统中的唯一实现:ServletWebServerFactoryCustomizer的customize方法。
  • customize把配置中的内容设置到ConfigurableServletWebServerFactory对象中。他的实现TomcatServletWebServerFactory在启动的时候就会有值!
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
    PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
    map.from(this.serverProperties::getPort).to(factory::setPort);
    map.from(this.serverProperties::getAddress).to(factory::setAddress);
    map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
    map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
    map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
    map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
    map.from(this.serverProperties::getSsl).to(factory::setSsl);
    map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
    map.from(this.serverProperties::getCompression).to(factory::setCompression);
    map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
    map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
    map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
    map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
    for (WebListenerRegistrar registrar : this.webListenerRegistrars) {
        registrar.register(factory);
    }
    if (!CollectionUtils.isEmpty(this.cookieSameSiteSuppliers)) {
        factory.setCookieSameSiteSuppliers(this.cookieSameSiteSuppliers);
    }
}

ServletWebServerFactoryCustomizer这个Bean是哪里的?

  • 在我们自动配置类ServletWebServerFactoryAutoConfiguration中定义。
?
1
2
3
4
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, ObjectProvider<WebListenerRegistrar> webListenerRegistrars, ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {
    return new ServletWebServerFactoryCustomizer(serverProperties,webListenerRegistrars.orderedStream().collect(Collectors.toList()),cookieSameSiteSuppliers.orderedStream().collect(Collectors.toList()));
}

到此这篇关于Spring boot整合tomcat底层原理的文章就介绍到这了,更多相关Spring boot整合tomcat内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/zfcq/p/16790236.html

延伸 · 阅读

精彩推荐
  • TomcatTomcat并发优化方法介绍

    Tomcat并发优化方法介绍

    这篇文章主要介绍了Tomcat并发优化方法介绍,小编觉得挺不错的,这里给大家分享下,供需要的朋友参考。...

    liuyazhuang6712021-08-26
  • TomcatTomcat安装配置方法图文教程

    Tomcat安装配置方法图文教程

    这篇文章主要为大家详细介绍了Tomcat安装配置方法图文教程,java环境变量如何配置,Eclipse安装配置方法图文教程 ,为大家分享了三个教程,感兴趣的小伙...

    Tomcat教程网12172021-08-11
  • TomcatTomcat 与 maven 的安装与使用教程

    Tomcat 与 maven 的安装与使用教程

    这篇文章主要介绍了Tomcat 与 maven 的安装与使用,Tomcat HTTP 服务器,按照 HTTP 协议的格式来解析请求,调用用户指定的相关代码,并且按照 HTTP 响应的格式来构...

    安陵容6152022-08-16
  • TomcatTomcat实现session共享(session 会话复制)

    Tomcat实现session共享(session 会话复制)

    这篇文章主要介绍了Tomcat实现session共享,其实就是session 会话复制的相关方法,需要的朋友可以参考下...

    namesam9222021-08-25
  • Tomcat解决tomcat部署下的web项目网页更改不能自动刷新的问题

    解决tomcat部署下的web项目网页更改不能自动刷新的问题

    下面小编就为大家带来一篇解决tomcat部署下的web项目网页更改不能自动刷新的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随...

    Tomcat教程网8322021-08-20
  • TomcatWindwos下实现Nginx+Tomcat集群过程解析

    Windwos下实现Nginx+Tomcat集群过程解析

    这篇文章主要介绍了Windwos下实现Nginx+Tomcat集群过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可...

    cuisuqiang11752021-09-17
  • TomcatLinux下Tomcat8.0.44配置使用Apr的方法

    Linux下Tomcat8.0.44配置使用Apr的方法

    下面小编就为大家带来一篇Linux下Tomcat8.0.44配置使用Apr的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    Tomcat教程网3712021-08-23
  • Tomcat详解Tomcat7中WebSocket初探

    详解Tomcat7中WebSocket初探

    随着Html5技术的普及,主流浏览器对HTML5标准的支持越来越好,利用浏览器原生支持WebSocket就可以轻松的实现上面的功能。有兴趣的可以了解一下。...

    PlusPlus17592021-08-17