SpringBoot2.4.0中嵌入式servlet容器的自動配置以及啟動原理(自我理解)
1.前言
在 springboot1.x 版本中,通過EmbeddedServletContainerAutoConfiguration來定製嵌入式的servlet容器,如下所示。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
//匯入BeanPostProcessorsRegistrar:給容器中匯入一些元件
//匯入了EmbeddedServletContainerCustomizerBeanPostProcessor
//後置處理器:bean初始化前後(建立完物件,還沒賦值賦值)執行初始化工作
public class EmbeddedServletContainerAutoConfiguration {
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })//判斷當前是否引入了Tomcat依賴;
@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
//判斷當前容器沒有使用者自己定義EmbeddedServletContainerFactory:嵌入式的 Servlet容器工廠;作用:建立嵌入式的Servlet容器
public static class EmbeddedTomcat {
@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
return new TomcatEmbeddedServletContainerFactory();
}
}
... // undertow 和 jetty 的配置
}
在 springboot2.x 版本中,通過EmbeddedWebServerFactoryCustomizerAutoConfiguration自動建立對應的WebServerFactoryCustomizer來定製servlet容器,在此記錄一下在這個版本中的嵌入式Servlet容器自動配置原理以及啟動原理。
2.自動配置流程
2.1.分析部分原始碼
這裡記錄在 springboot2.x 版本中,嵌入式Servlet容器自動配置原理(以Tomcat為例)。
1.首先我們進入EmbeddedWebServerFactoryCustomizerAutoConfiguration的原始碼,發現 springboot2.4.0 支援四種servlet容器,從上到下分別為tomcat、jetty、undertow、netty。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
public static class TomcatWebServerFactoryCustomizerConfiguration {
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
public static class JettyWebServerFactoryCustomizerConfiguration {
@Bean
public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new JettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
public static class UndertowWebServerFactoryCustomizerConfiguration {
@Bean
public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpServer.class)
public static class NettyWebServerFactoryCustomizerConfiguration {
@Bean
public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment,
ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
}
2.本篇以tomcat為例,所以我們點進TomcatWebServerFactoryCustomizer(TomcatWeb伺服器工廠定製器)進行檢視。發現它應該是呼叫customize()定製介面,定製Servlet容器配置。
public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
private final ServerProperties serverProperties;
@Override
public void customize(ConfigurableTomcatWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
PropertyMapper propertyMapper = PropertyMapper.get();
propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds)
.as(Long::intValue).to(factory::setBackgroundProcessorDelay);
customizeRemoteIpValve(factory);
ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads();
propertyMapper.from(threadProperties::getMax).when(this::isPositive).to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax()));
...
customizeStaticResources(factory);
customizeErrorReportValve(properties.getError(), factory);
}
}
此時我們大致瞭解,那我們就從入口開始分析。
2.2.執行程式時的流程
1.首先執行程式的main()方法,然後會呼叫run()方法。
2.執行run方法時,裡面會建立並初始化我們的IOC容器物件。
3.在建立ApplicationContext的IOC容器物件時,在ServletWebServerApplicationContext有一個onRefresh()的方法,通過呼叫createWebServer()方法,建立WebServer。
4.來到createWebServer()方法,該方法通過裡面的getWebServerFactory()方法,通過名稱匹配最終能夠獲取到一個與當前應用(根據我們匯入的依賴servlet容器來獲取)所匯入的Servlet型別相匹配的web服務工廠,通過工廠就可以獲取到相應的 WebServerFactoryCustomizer (Web服務工廠定製器)
注:createWebServer()執行後,我們就到了 EmbeddedWebServerFactoryCustomizerAutoConfiguration,然後根據條件(配置的依賴)配置哪一個Web伺服器
5.我們點進factory.getWebServer()方法,找到了ServletWebServerFactory介面,因為我們前面繫結了tomcat,所以這裡會建立它的實現類TomcatServletWebServerFactory。
到這裡位置,TomcatWebServerFactoryCustomizer元件建立完成,對應的服務配置類也已新增到IOC容器。
6.因為容器中某個元件要建立物件就會驚動後置處理器 然後就到WebServerFactoryCustomizerBeanPostProcessor(web服務工廠定製器元件的後置處理器),該類負責在bean元件初始化之前執行初始化工作。它先從IOC容器中獲取所有型別為WebServerFactoryCustomizerBeans(web服務工廠定製器的元件)
再通過後置處理器獲取到的TomcatWebServerFactoryCustomizer呼叫**customize()**定製方法,獲取到Servlet容器相關配置類ServerProperties,進行自動配置
@Override
public void customize(ConfigurableTomcatWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
PropertyMapper propertyMapper = PropertyMapper.get();
propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds)
.as(Long::intValue).to(factory::setBackgroundProcessorDelay);
customizeRemoteIpValve(factory);
ServerProperties.Tomcat.Threads threadProperties = tomcatProperties.getThreads();
propertyMapper.from(threadProperties::getMax).when(this::isPositive)
.to((maxThreads) -> customizeMaxThreads(factory, threadProperties.getMax()));
propertyMapper.from(threadProperties::getMinSpare).when(this::isPositive)
.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull().asInt(DataSize::toBytes)
.when(this::isPositive)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize));
propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull().asInt(DataSize::toBytes)
.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
propertyMapper.from(tomcatProperties::getMaxHttpFormPostSize).asInt(DataSize::toBytes)
.when((maxHttpFormPostSize) -> maxHttpFormPostSize != 0)
.to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize));
propertyMapper.from(tomcatProperties::getAccesslog).when(ServerProperties.Tomcat.Accesslog::isEnabled)
.to((enabled) -> customizeAccessLog(factory));
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
propertyMapper.from(tomcatProperties::getProcessorCache)
.to((processorCache) -> customizeProcessorCache(factory, processorCache));
propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText()
.to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars));
propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText()
.to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars));
customizeStaticResources(factory);
customizeErrorReportValve(properties.getError(), factory);
}
到這裡為止,嵌入式Servlet容器的自動配置完成。
綜上所述,我們有兩種解決方案用於配置嵌入式Servlet容器。
1.在全域性配置檔案application.properties中修改與server有關的配置
server.port=8090
server.tomcat.uri-encoding=UTF-8
server.tomcat.xxx…
2、實現WebServerFactoryCustomizer介面,重寫它的customize()方法,對容器進行定製配置:
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
void customize(T factory);
}
3.嵌入式servlet容器啟動原理
1、應用啟動後,根據匯入的依賴資訊,建立了相應的Servlet容器工廠,建立了TomcatServletWebServerFactory,呼叫重寫的getWebServer()方法建立Tomcat容器。
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
...
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
...
}
2.找到這個方法的返回值中this.getTomcatWebServer(tomcat),繼續向下尋找該方法
3.進入TomcatWebServer檢視它的有參構造器,發現其中執行了一個initialize() 初始化方法,跟進檢視。
4.通過 this.tomcat.start(); tomcat啟動了。
相關文章
- 十二、Spring Boot 嵌入式 Servlet 容器啟動原理Spring BootServlet
- Docker容器的自啟動Docker
- SpringBoot自動配置原理解析Spring Boot
- springboot自動配置原理和啟動流程Spring Boot
- Docker重啟保持容器自動啟動Docker
- SpringBoot-04-自動配置原理再理解Spring Boot
- Spring容器系列-啟動原理Spring
- Spring Boot 自動配置的原理、核心註解以及利用自動配置實現了自定義 Starter 元件Spring Boot元件
- docker 啟動 centos 映象,容器會自動退出DockerCentOS
- SpringBoot的自動配置原理Spring Boot
- Ubuntu自動啟動配置指令碼Ubuntu指令碼
- 使用laradock啟動容器elasticsearch遇到啟動後幾秒鐘又自動關閉的問題以及解決方法Elasticsearch
- (第四講)Spring Boot 自動化配置原理解析Spring Boot
- Spring 容器與 Servlet互動SpringServlet
- SpringBoot | 自動配置原理Spring Boot
- SpringBoot自動配置原理Spring Boot
- Spring Boot 自動配置原理Spring Boot
- springboot 自動配置原理Spring Boot
- docker中怎麼啟動容器Docker
- MySQL讀取配置檔案的順序、啟動方式、啟動原理MySql
- 圖解原始碼 | SpringBoot中自動配置原理圖解原始碼Spring Boot
- oracle配置開機自啟動Oracle
- 如何理解Java中的自動拆箱和自動裝箱?Java
- PHP 自動載入功能原理解析PHP
- springboot_自動配置原理Spring Boot
- Spring Boot核心原理-自動配置Spring Boot
- [spring-rabbit]自動配置原理Spring
- 4 配置Oracle資料庫自動啟動Oracle資料庫
- linux 配置oracle+asm自動啟動LinuxOracleASM
- Orale自動啟動以及關閉指令碼_linux指令碼Linux
- Servlet 工作原理解析Servlet
- springboot之啟動原理解析Spring Boot
- 如何在Docker容器啟動時自動執行指令碼Docker指令碼
- Spring Boot 啟動原理解析(二) Tomcat 啟動詳解Spring BootTomcat
- springboot 配置熱啟動 不需重啟自動部署Spring Boot
- 啟動zabbix容器
- Docker(十七)-修改Docker容器啟動配置引數Docker
- 嵌入式linux wpa_supplicant自動配置程式Linux