Spring?boot整合tomcat底層原理剖析
從源碼層面理解spring boot的默認(rèn)web容器,以及他們是如何關(guān)聯(lián)起來的。
本文結(jié)論
- 源碼基于spring boot2.6.6
- 項(xiàng)目的pom.xml中存在spring-boot-starter-web的時(shí)候,在項(xiàng)目啟動(dòng)時(shí)候就會(huì)自動(dòng)啟動(dòng)一個(gè)Tomcat。
- 自動(dòng)配置類ServletWebServerFactoryAutoConfiguration找到系統(tǒng)中的所有web容器。我們以tomcat為主。
- 構(gòu)建TomcatServletWebServerFactory的bean。
- SpringBoot的啟動(dòng)過程中,會(huì)調(diào)用核心的refresh方法,內(nèi)部會(huì)執(zhí)行onRefresh()方法,onRefresh()方法是一個(gè)模板方法,他會(huì)執(zhí)行會(huì)執(zhí)行子類ServletWebServerApplicationContext的onRefresh()方法。
- onRefresh()方法中調(diào)用getWebServer啟動(dòng)web容器。
spring-boot-starter-web內(nèi)部有什么?
- 在spring-boot-starter-web這個(gè)starter中,其實(shí)內(nèi)部間接的引入了spring-boot-starter-tomcat這個(gè)starter,這個(gè)spring-boot-starter-tomcat又引入了tomcat-embed-core依賴,所以只要我們項(xiàng)目中依賴了spring-boot-starter-web就相當(dāng)于依賴了Tomcat。


自動(dòng)配置類:ServletWebServerFactoryAutoConfiguration在spring-boot-autoconfigure-2.6.6.jar這個(gè)包中的spring.factories文件內(nèi),配置了大量的自動(dòng)配置類,其中就包括自動(dòng)配置tomcat的自動(dòng)配置類:ServletWebServerFactoryAutoConfiguration

自動(dòng)配置類的代碼如下
// full模式
@Configuration(proxyBeanMethods = false)
// 配置類解析順序
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 條件注解:表示項(xiàng)目依賴中要有ServletRequest類(server api)
@ConditionalOnClass(ServletRequest.class)
// 表示項(xiàng)目應(yīng)用類型得是SpringMVC(在啟動(dòng)過程中獲取的SpringBoot應(yīng)用類型)
@ConditionalOnWebApplication(type = Type.SERVLET)
// 讀取server下的配置文件
@EnableConfigurationProperties(ServerProperties.class)
// import具體的加載配置的類和具體web實(shí)現(xiàn)容器
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
......
}
- ServletRequest是存在于tomcat-embed-core-9.0.60.jar中的的一個(gè)類,所以@ConditionalOnClass(ServletRequest.clas s)會(huì)滿足。
- spring-boot-starter-web中,間接的引入了spring-web、spring-webmvc等依賴,所以@ConditionalOnWebApplication(type = Type.SERVLET)條件滿足。
- 上面的倆個(gè)條件都滿足,所以spring回去解析這個(gè)配置類,在解析過程中會(huì)發(fā)現(xiàn)他import了三個(gè)類!我們重點(diǎn)關(guān)注EmbeddedTomcat。其他倆個(gè)的內(nèi)部條件注解不滿足!
@Configuration(proxyBeanMethods = false)
// tomcat內(nèi)部的類,肯定都存在
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
// 程序員如果自定義了ServletWebServerFactory的Bean,那么這個(gè)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()調(diào)用時(shí)會(huì)去Spring容器中找到TomcatConnectorCustomizer類型的Bean,默認(rèn)是沒有的,程序員可以自己定義。這個(gè)Bean可以設(shè)置一些tomcat的配置,比如端口、協(xié)議...
// 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;
}
}
}- 對(duì)于另外的EmbeddedJetty和EmbeddedUndertow,邏輯類似,都是判斷項(xiàng)目依賴中是否有Jetty和Undertow的依賴,如果有,那么對(duì)應(yīng)在Spring容器中就會(huì)存在JettyServletWebServerFactory類型的Bean、或者存在UndertowServletWebServerFactory類型的Bean。
TomcatServletWebServerFactory的作用:獲取WebServer對(duì)象
- TomcatServletWebServerFactory他實(shí)現(xiàn)了ServletWebServerFactory這個(gè)接口。
- ServletWebServerFactory接口內(nèi)部只有一個(gè)方法是獲取WebServer對(duì)象。

- WebServer擁有啟動(dòng)、停止、獲取端口等方法,就會(huì)發(fā)現(xiàn)WebServer其實(shí)指的就是Tomcat、Jetty、Undertow。

- 而TomcatServletWebServerFactory就是用來生成Tomcat所對(duì)應(yīng)的WebServer對(duì)象,具體一點(diǎn)就是TomcatWebServer對(duì)象,并且在生成TomcatWebServer對(duì)象時(shí)會(huì)把Tomcat給啟動(dòng)起來。
- 在源碼中,調(diào)用TomcatServletWebServerFactory對(duì)象的getWebServer()方法時(shí)就會(huì)啟動(dòng)Tomcat。
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
// 構(gòu)建tomcat對(duì)象
Tomcat tomcat = new Tomcat();
// 設(shè)置相關(guān)的屬性
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);
// 啟動(dòng)tomcat,這個(gè)方法內(nèi)部有this.tomcat.start();
return getTomcatWebServer(tomcat);
}
spring boot啟動(dòng)的時(shí)候啟動(dòng)tomcat
- SpringBoot的啟動(dòng)過程中,會(huì)調(diào)用核心的refresh方法,內(nèi)部會(huì)執(zhí)行onRefresh()方法,onRefresh()方法是一個(gè)模板方法,他會(huì)執(zhí)行會(huì)執(zhí)行子類ServletWebServerApplicationContext的onRefresh()方法。
protected void onRefresh() {
// 模板方法,先調(diào)用它父類的,一般是空方法
super.onRefresh();
try {
// 創(chuàng)建web容器
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}這個(gè)方法會(huì)調(diào)用createWebServer()方法。
// 最核心的倆行代碼
private void createWebServer() {
......
// 獲取web容器,多個(gè)或者沒有的時(shí)候報(bào)錯(cuò)
ServletWebServerFactory factory = getWebServerFactory();
// 調(diào)用這個(gè)容器的getWebServer方法,上面的啟動(dòng)tomcat的方法!
this.webServer = factory.getWebServer(getSelfInitializer());
......
}- getWebServerFactory控制項(xiàng)目組有且只能有一個(gè)web容器!
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);
// 不存在,報(bào)錯(cuò)
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
}
// 存在不止一個(gè),報(bào)錯(cuò)!
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
// 返回唯一的一個(gè)web容器!
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
獲取tomcat的配置
- 自動(dòng)配置類ServletWebServerFactoryAutoConfiguration上除了import三個(gè)web容器,還import了BeanPostProcessorsRegistrar。
- BeanPostProcessorsRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar,所以他會(huì)在spring啟動(dòng)的時(shí)候調(diào)用registerBeanDefinitions方法。
- registerBeanDefinitions會(huì)注冊(cè)一個(gè)Bean:webServerFactoryCustomizerBeanPostProcessor。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// Bean工廠,一個(gè)Aware回調(diào)進(jìn)行賦值
if (this.beanFactory == null) {
return;
}
// 注冊(cè)webServerFactoryCustomizerBeanPostProcessor這個(gè)Bean。
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class,
WebServerFactoryCustomizerBeanPostProcessor::new);
// 注冊(cè)errorPageRegistrarBeanPostProcessor
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
}- webServerFactoryCustomizerBeanPostProcessor實(shí)現(xiàn)了BeanPostProcessor,所以他會(huì)在啟動(dòng)的時(shí)候調(diào)用postProcessBeforeInitialization方法。
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
// 找到WebServerFactoryCustomizer的Bean
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
// 標(biāo)記日志用的類
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
// 調(diào)用customize方法,傳入webServerFactory
.invoke((customizer) -> customizer.customize(webServerFactory));
}- postProcessBeforeInitialization中會(huì)調(diào)用WebServerFactoryCustomizer類customize方法,在系統(tǒng)中的唯一實(shí)現(xiàn):ServletWebServerFactoryCustomizer的customize方法。
- customize把配置中的內(nèi)容設(shè)置到ConfigurableServletWebServerFactory對(duì)象中。他的實(shí)現(xiàn)TomcatServletWebServerFactory在啟動(dòng)的時(shí)候就會(huì)有值!
@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這個(gè)Bean是哪里的?
- 在我們自動(dòng)配置類ServletWebServerFactoryAutoConfiguration中定義。
@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()));
}到此這篇關(guān)于Spring boot整合tomcat底層原理的文章就介紹到這了,更多相關(guān)Spring boot整合tomcat內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
tomcat雙擊startup.bat一閃而過(閃退)的問題解決
本文主要介紹了tomcat雙擊startup.bat一閃而過(閃退)的問題解決,可能是tomcat找不到你的jdk環(huán)境配置路徑,下面就來介紹一下解決方法,感興趣的可以了解一下2024-05-05
Tomcat 檢測(cè)內(nèi)存泄漏實(shí)例詳解
這篇文章主要介紹了 Tomcat 檢測(cè)內(nèi)存泄漏實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12
使用IDEA配置tomcat及創(chuàng)建JSP文件的方法
這篇文章主要介紹了使用IDEA配置tomcat及創(chuàng)建JSP文件的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
學(xué)習(xí)javaweb如何配置Tomcat的熱啟動(dòng)
學(xué)習(xí)javaweb的時(shí)候每次更改項(xiàng)目都需要重新部署項(xiàng)目,如此一來比較麻煩,使用tomcat的熱啟動(dòng)就可以解決這個(gè)問題2014-09-09
startup.bat啟動(dòng)Tomcat閃退問題原因及解決
本文主要介紹了startup.bat啟動(dòng)Tomcat閃退問題原因及解決,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
解析Tomcat的啟動(dòng)腳本--startup.bat
本文主要對(duì)Tomcat的三個(gè)最重要的啟動(dòng)腳本之一--startup.bat腳本做了詳細(xì)分析,具有很好的參考價(jià)值,需要的朋友可以看下2016-12-12
關(guān)于tomcat下啟動(dòng)solr遇到的問題及解決
這篇文章主要介紹了關(guān)于tomcat下啟動(dòng)solr遇到的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07

