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的時候,在項(xiàng)目啟動時候就會自動啟動一個Tomcat。
- 自動配置類ServletWebServerFactoryAutoConfiguration找到系統(tǒng)中的所有web容器。我們以tomcat為主。
- 構(gòu)建TomcatServletWebServerFactory的bean。
- SpringBoot的啟動過程中,會調(diào)用核心的refresh方法,內(nèi)部會執(zhí)行onRefresh()方法,onRefresh()方法是一個模板方法,他會執(zhí)行會執(zhí)行子類ServletWebServerApplicationContext的onRefresh()方法。
- onRefresh()方法中調(diào)用getWebServer啟動web容器。
spring-boot-starter-web內(nèi)部有什么?
- 在spring-boot-starter-web這個starter中,其實(shí)內(nèi)部間接的引入了spring-boot-starter-tomcat這個starter,這個spring-boot-starter-tomcat又引入了tomcat-embed-core依賴,所以只要我們項(xiàng)目中依賴了spring-boot-starter-web就相當(dāng)于依賴了Tomcat。
自動配置類:ServletWebServerFactoryAutoConfiguration在spring-boot-autoconfigure-2.6.6.jar這個包中的spring.factories文件內(nèi),配置了大量的自動配置類,其中就包括自動配置tomcat的自動配置類:ServletWebServerFactoryAutoConfiguration
自動配置類的代碼如下
// full模式 @Configuration(proxyBeanMethods = false) // 配置類解析順序 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 條件注解:表示項(xiàng)目依賴中要有ServletRequest類(server api) @ConditionalOnClass(ServletRequest.class) // 表示項(xiàng)目應(yīng)用類型得是SpringMVC(在啟動過程中獲取的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中的的一個類,所以@ConditionalOnClass(ServletRequest.clas s)會滿足。
- spring-boot-starter-web中,間接的引入了spring-web、spring-webmvc等依賴,所以@ConditionalOnWebApplication(type = Type.SERVLET)條件滿足。
- 上面的倆個條件都滿足,所以spring回去解析這個配置類,在解析過程中會發(fā)現(xiàn)他import了三個類!我們重點(diǎn)關(guān)注EmbeddedTomcat。其他倆個的內(nèi)部條件注解不滿足!
@Configuration(proxyBeanMethods = false) // tomcat內(nèi)部的類,肯定都存在 @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()調(diào)用時會去Spring容器中找到TomcatConnectorCustomizer類型的Bean,默認(rèn)是沒有的,程序員可以自己定義。這個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; } } }
- 對于另外的EmbeddedJetty和EmbeddedUndertow,邏輯類似,都是判斷項(xiàng)目依賴中是否有Jetty和Undertow的依賴,如果有,那么對應(yīng)在Spring容器中就會存在JettyServletWebServerFactory類型的Bean、或者存在UndertowServletWebServerFactory類型的Bean。
TomcatServletWebServerFactory的作用:獲取WebServer對象
- TomcatServletWebServerFactory他實(shí)現(xiàn)了ServletWebServerFactory這個接口。
- ServletWebServerFactory接口內(nèi)部只有一個方法是獲取WebServer對象。
- WebServer擁有啟動、停止、獲取端口等方法,就會發(fā)現(xiàn)WebServer其實(shí)指的就是Tomcat、Jetty、Undertow。
- 而TomcatServletWebServerFactory就是用來生成Tomcat所對應(yīng)的WebServer對象,具體一點(diǎn)就是TomcatWebServer對象,并且在生成TomcatWebServer對象時會把Tomcat給啟動起來。
- 在源碼中,調(diào)用TomcatServletWebServerFactory對象的getWebServer()方法時就會啟動Tomcat。
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } // 構(gòu)建tomcat對象 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); // 啟動tomcat,這個方法內(nèi)部有this.tomcat.start(); return getTomcatWebServer(tomcat); }
spring boot啟動的時候啟動tomcat
- SpringBoot的啟動過程中,會調(diào)用核心的refresh方法,內(nèi)部會執(zhí)行onRefresh()方法,onRefresh()方法是一個模板方法,他會執(zhí)行會執(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); } }
這個方法會調(diào)用createWebServer()方法。
// 最核心的倆行代碼 private void createWebServer() { ...... // 獲取web容器,多個或者沒有的時候報(bào)錯 ServletWebServerFactory factory = getWebServerFactory(); // 調(diào)用這個容器的getWebServer方法,上面的啟動tomcat的方法! this.webServer = factory.getWebServer(getSelfInitializer()); ...... }
- getWebServerFactory控制項(xiàng)目組有且只能有一個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)錯 if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean."); } // 存在不止一個,報(bào)錯! 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實(shí)現(xiàn)了ImportBeanDefinitionRegistrar,所以他會在spring啟動的時候調(diào)用registerBeanDefinitions方法。
- registerBeanDefinitions會注冊一個Bean:webServerFactoryCustomizerBeanPostProcessor。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // Bean工廠,一個Aware回調(diào)進(jìn)行賦值 if (this.beanFactory == null) { return; } // 注冊webServerFactoryCustomizerBeanPostProcessor這個Bean。 registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class, WebServerFactoryCustomizerBeanPostProcessor::new); // 注冊errorPageRegistrarBeanPostProcessor registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new); }
- webServerFactoryCustomizerBeanPostProcessor實(shí)現(xiàn)了BeanPostProcessor,所以他會在啟動的時候調(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中會調(diào)用WebServerFactoryCustomizer類customize方法,在系統(tǒng)中的唯一實(shí)現(xiàn):ServletWebServerFactoryCustomizer的customize方法。
- customize把配置中的內(nèi)容設(shè)置到ConfigurableServletWebServerFactory對象中。他的實(shí)現(xiàn)TomcatServletWebServerFactory在啟動的時候就會有值!
@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中定義。
@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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
教你在IDEA中使用Tomcat部署并啟動Web項(xiàng)目
這篇文章主要介紹了教你在IDEA中使用Tomcat部署并啟動Web項(xiàng)目,IDEA作為一款主流的開發(fā)工具其實(shí)已經(jīng)集成了Tomcat的部分功能,需要的朋友可以參考下2023-03-03Tomcat報(bào)錯: JDBC unregister 解決辦法
這篇文章主要介紹了Tomcat報(bào)錯: JDBC unregister 解決辦法的相關(guān)資料,需要的朋友可以參考下2017-05-05Linux下定時切割Tomcat日志并刪除指定天數(shù)前的日志記錄
這篇文章主要介紹了Linux下定時切割Tomcat日志并刪除指定天數(shù)前的日志記錄,需要的朋友可以參考下2017-08-08Tomcat配置訪問日志和線程數(shù)的實(shí)現(xiàn)步驟
本文主要介紹了Tomcat配置訪問日志和線程數(shù)的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05Centos8.2云服務(wù)器環(huán)境安裝Tomcat8.5的詳細(xì)教程
這篇文章主要介紹了Centos8.2云服務(wù)器環(huán)境安裝Tomcat8.5的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12tomcat簡介_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了tomcat簡介,詳細(xì)的介紹了tomcat和Tomcat基本配置,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07