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 }
那些看似简单的事物,其实并不简单。我们之所以觉得他简单,是因为复杂性都被隐藏了。通过上诉代码,大概率可以提出以下几个疑问
- SpringBoot是如何启动内置tomcat的 - 文章的重点
- 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