首页 > 编程语言 >很简单的源码剖析-SpringBoot内嵌Tomcat原理

很简单的源码剖析-SpringBoot内嵌Tomcat原理

时间:2022-12-06 20:33:06浏览次数:68  
标签:propertyMapper getClass SpringBoot Tomcat tomcatProperties factory 源码 tomcat

Spring Boot默认支持Tomcat,Jetty,和Undertow作为底层容器。而Spring Boot默认使用Tomcat,一旦引入spring-boot-starter-web模块,就默认使用Tomcat容器。

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

在启动springboot的时候可谓是相当简单,只需要执行以下代码:

1 @SpringBootApplication
2 public class SpringBootMyTestApplication {
3    public static void main(String[] args) {
4       SpringApplication.run(SpringBootMyTestApplication.class, args);
5    }
6 }

那些看似简单的事物,其实并不简单。我们之所以觉得他简单,是因为复杂性都被隐藏了。通过上诉代码,大概率可以提出以下几个疑问

  1. SpringBoot是如何启动内置tomcat的 - 文章的重点
  2. SpringBoot为什么可以响应请求,他是如何配置的SpringMVC

这次的重点是先分享SpringBoot是如何启动内置的Tomcat的

1.内嵌Tomcat自动配置原理

1.1 Tomcat服务自动配置类

在SpringBoot的启动过程中会自动加载各个模块下的META-INF/spring.factories文件中定义的自动配置类,Tomcat的服务的加载也是如此,所以首先要找到加载的自动配置,如下图所示:

找到这个配置类进去看一下实现的内容:

可以看到里面也通过@Import注解将EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow等嵌入式容器类加载进来了,springboot默认是启动嵌入式tomcat容器,如果要改变启动jetty或者undertow容器,需在pom文件中去设置。
这里默认实现的是Tomcat容器,那么看一下EmbeddedTomcat:

进入TomcatServletWebServerFactory类,里面的getWebServer()是关键方法,如图:

继续进入getTomcatWebServer()等方法,一直往下跟到tomcat初始化方法,调用tomcat.start()方法,tomcat就正式开启运行,见图:

走到这里tomcat在springboot中的配置以及最终启动的流程就走完了。

1.2 SpringBoot启动Tomcat

在SpringBoot启动过程中有一个很重要的步骤:

1 // 刷新应用上下文
2 refreshContext(context);

内置tomcat的启动就是在这个方法中进行调用的,点击实现的逻辑,最终进到了Spring的源码中:

上面的这个onRefresh()方法就是关键点,点击进行查看源码,onRefresh()会调用到ServletWebServerApplicationContext中的createWebServer(),

 1 private void createWebServer() {
 2    WebServer webServer = this.webServer;
 3    ServletContext servletContext = getServletContext();
 4    if (webServer == null && servletContext == null) {
 5       ServletWebServerFactory factory = getWebServerFactory();
 6       this.webServer = factory.getWebServer(getSelfInitializer());
 7    }
 8    else if (servletContext != null) {
 9       try {
10          getSelfInitializer().onStartup(servletContext);
11       }
12       catch (ServletException ex) {
13          throw new ApplicationContextException("Cannot initialize servlet context", ex);
14       }
15    }
16    initPropertySources();
17 }

createWebServer()就是启动web服务,但是还没有真正启动Tomcat,既然webServer是通过ServletWebServerFactory来获取的,先来看一下getWebServerFactory()方法的实现:

 1 protected ServletWebServerFactory getWebServerFactory() {
 2    // Use bean names so that we don't consider the hierarchy
 3    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
 4    if (beanNames.length == 0) {
 5       throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
 6             + "ServletWebServerFactory bean.");
 7    }
 8    if (beanNames.length > 1) {
 9       throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
10             + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
11    }
12    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
13 }

看最后一步的返回,因为这里使用的是tomcat容器,所以最终返回的就是一个TomcatServletWebServerFactory实例,最终就调用了TomcatServletWebServerFactory类的getWebServer()方法,那么也就实现了tomcat服务的启动。
debug验证一下上述的流程:

2.内嵌Tomcat配置参数原理

2.1 springboot配置管理

springboot内嵌web容器配置的配置参数主要是在ServerProperties.java中,以内嵌Tomcat为例:

 

 

 可以看到,Tomcat默认的connections为8192,acceptCount为100

2.2 Tomcat个性化配置

从1.1中可以知道,tomcat的个性化配置的加载主要是在EmbeddedTomcat的tomcatServletWebServerFactory中:

 

 

 其中,主要的Customizer为TomcatWebServerFactoryCustomizer,主要方法为customize:

 1     public void customize(ConfigurableTomcatWebServerFactory factory) {
 2         ServerProperties properties = this.serverProperties;
 3         Tomcat tomcatProperties = properties.getTomcat();
 4         PropertyMapper propertyMapper = PropertyMapper.get();
 5         tomcatProperties.getClass();
 6         propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
 7         tomcatProperties.getClass();
 8         propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds).as(Long::intValue).to(factory::setBackgroundProcessorDelay);
 9         this.customizeRemoteIpValve(factory);
10         Threads threadProperties = tomcatProperties.getThreads();
11         threadProperties.getClass();
12         propertyMapper.from(threadProperties::getMax).when(this::isPositive).to((maxThreads) -> {
13             this.customizeMaxThreads(factory, threadProperties.getMax());
14         });
15         threadProperties.getClass();
16         propertyMapper.from(threadProperties::getMinSpare).when(this::isPositive).to((minSpareThreads) -> {
17             this.customizeMinThreads(factory, minSpareThreads);
18         });
19         propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull().asInt(DataSize::toBytes).when(this::isPositive).to((maxHttpHeaderSize) -> {
20             this.customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
21         });
22         tomcatProperties.getClass();
23         propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull().asInt(DataSize::toBytes).to((maxSwallowSize) -> {
24             this.customizeMaxSwallowSize(factory, maxSwallowSize);
25         });
26         tomcatProperties.getClass();
27         propertyMapper.from(tomcatProperties::getMaxHttpFormPostSize).asInt(DataSize::toBytes).when((maxHttpFormPostSize) -> {
28             return maxHttpFormPostSize != 0;
29         }).to((maxHttpFormPostSize) -> {
30             this.customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize);
31         });
32         tomcatProperties.getClass();
33         propertyMapper.from(tomcatProperties::getAccesslog).when(Accesslog::isEnabled).to((enabled) -> {
34             this.customizeAccessLog(factory);
35         });
36         tomcatProperties.getClass();
37         propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
38         tomcatProperties.getClass();
39         propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
40             this.customizeConnectionTimeout(factory, connectionTimeout);
41         });
42         tomcatProperties.getClass();
43         propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> {
44             this.customizeMaxConnections(factory, maxConnections);
45         });
46         tomcatProperties.getClass();
47         propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> {
48             this.customizeAcceptCount(factory, acceptCount);
49         });
50         tomcatProperties.getClass();
51         propertyMapper.from(tomcatProperties::getProcessorCache).to((processorCache) -> {
52             this.customizeProcessorCache(factory, processorCache);
53         });
54         tomcatProperties.getClass();
55         propertyMapper.from(tomcatProperties::getKeepAliveTimeout).whenNonNull().to((keepAliveTimeout) -> {
56             this.customizeKeepAliveTimeout(factory, keepAliveTimeout);
57         });
58         tomcatProperties.getClass();
59         propertyMapper.from(tomcatProperties::getMaxKeepAliveRequests).to((maxKeepAliveRequests) -> {
60             this.customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests);
61         });
62         tomcatProperties.getClass();
63         propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText().to((relaxedChars) -> {
64             this.customizeRelaxedPathChars(factory, relaxedChars);
65         });
66         tomcatProperties.getClass();
67         propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText().to((relaxedChars) -> {
68             this.customizeRelaxedQueryChars(factory, relaxedChars);
69         });
70         tomcatProperties.getClass();
71         propertyMapper.from(tomcatProperties::isRejectIllegalHeader).to((rejectIllegalHeader) -> {
72             this.customizeRejectIllegalHeader(factory, rejectIllegalHeader);
73         });
74         this.customizeStaticResources(factory);
75         this.customizeErrorReportValve(properties.getError(), factory);
76     }

以ConnectionTimeout的配置为例:

 1     private void customizeConnectionTimeout(ConfigurableTomcatWebServerFactory factory, Duration connectionTimeout) {
 2         factory.addConnectorCustomizers(new TomcatConnectorCustomizer[]{(connector) -> {
 3             ProtocolHandler handler = connector.getProtocolHandler();
 4             if (handler instanceof AbstractProtocol) {
 5                 AbstractProtocol<?> protocol = (AbstractProtocol)handler;
 6                 protocol.setConnectionTimeout((int)connectionTimeout.toMillis());
 7             }
 8 
 9         }});
10     }

然后看一下AbstractProtocol的setConnectionTimeout方法:

 1     public int getConnectionTimeout() {
 2         return this.endpoint.getConnectionTimeout();
 3     }
 4 
 5     public void setConnectionTimeout(int timeout) {
 6         this.endpoint.setConnectionTimeout(timeout);
 7     }
 8 
 9     public long getConnectionCount() {
10         return this.endpoint.getConnectionCount();
11     }

接着看一下AbstarctEndpoint的setConnectionTimeout:

1     public int getConnectionTimeout() {
2         return this.socketProperties.getSoTimeout();
3     }
4 
5     public void setConnectionTimeout(int soTimeout) {
6         this.socketProperties.setSoTimeout(soTimeout);
7     }

可以看到最后将socket的配置进行了修改

3.总结

附上源码流程图:

 

 参考:https://blog.csdn.net/lengyingzhang/article/details/125545981

 

标签:propertyMapper,getClass,SpringBoot,Tomcat,tomcatProperties,factory,源码,tomcat
From: https://www.cnblogs.com/fnlingnzb-learner/p/16960431.html

相关文章