日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【springboot】自动整合Tomcat原理

發布時間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【springboot】自动整合Tomcat原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通過前面我們會SpringBoot的自動配置機制、Starter機制、啟動過程的底層分析,我們拿一個實際的業務案例來串講一下,那就是SpringBoot和Tomcat的整合。

我們知道,只要我們的項目添加的starter為:spring-boot-starter-web,那么我們啟動項目時,SpringBoot就會自動啟動一個Tomcat。

那么這是怎么做到的呢?

自動配置類ServletWebServerFactoryAutoConfiguration

首先我們可以發現,在spring-boot-starter-web這個starter中,只是簡單的引入了spring-boot-starter-tomcat這個starter,這個spring-boot-starter-tomcat又引入了tomcat-embed-core依賴,所以只要我們項目中依賴了spring-boot-starter-web就相當于依賴了Tomcat。

然后在SpringBoot眾多的自動配置類中,有一個自動配置類叫做ServletWebServerFactoryAutoConfiguration,定義為:

@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { // ... }

首先看這個自動配置類所需要的條件:

  • @ConditionalOnClass(ServletRequest.class):表示項目依賴中要有ServletRequest類(server api)
  • @ConditionalOnWebApplication(type = Type.SERVLET):表示項目應用類型得是SpringMVC
  • 在上面提到的spring-boot-starter-web中,其實還間接的引入了spring-web、spring-webmvc等依賴,這就使得第二個條件滿足,而對于第一個條件的ServletRequest類,雖然它是Servlet規范中的類,但是在我們所依賴的tomcat-embed-core這個jar包中是存在這個類的,這是因為Tomcat在自己的源碼中把Servlet規范中的一些代碼也包含進去了,比如:

    這就使得ServletWebServerFactoryAutoConfiguration這個自動配置的兩個條件都符合,那么Spring就能去解析它,一解析它就發現這個自動配置類Import進來了三個類:

  • ServletWebServerFactoryConfiguration.EmbeddedTomcat.class
  • ServletWebServerFactoryConfiguration.EmbeddedJetty.class
  • ServletWebServerFactoryConfiguration.EmbeddedUndertow.class
  • EmbeddedTomcat

    很明顯,Import進來的這三個類應該是差不多,我們看EmbeddedTomcat這個類:

    @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat {@BeanTomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory;}}

    可以發現這個類是一個配置類,所以Spring也會來解析它,不過它也有兩個條件:

  • @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }):項目依賴中要有Servlet.class、Tomcat.class、UpgradeProtocol.class這三個類,這個條件比較容易理解,項目依賴中有Tomcat的類,那這個條件就符合。
  • @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT),項目中沒有ServletWebServerFactory類型的Bean,因為這個配置類的內部就是定義了一個TomcatServletWebServerFactory類型的Bean,TomcatServletWebServerFactory實現了ServletWebServerFactory接口,所以這個條件注解的意思就是,如果程序員自己沒有定義ServletWebServerFactory類型的Bean,那么就符合條件,不然,如果程序員自己定義了ServletWebServerFactory類型的Bean,那么條件就不符合,也就導致SpringBoot給我們定義的TomcatServletWebServerFactory這個Bean就不會生效,最終生效的就是程序員自己定義的。
  • 所以,通常只要我們項目依賴中有Tomcat依賴,那就符合條件,那最終Spring容器中就會有TomcatServletWebServerFactory這個Bean。

    對于另外的EmbeddedJetty和EmbeddedUndertow,也差不多,都是判斷項目依賴中是否有Jetty和Undertow的依賴,如果有,那么對應在Spring容器中就會存在JettyServletWebServerFactory類型的Bean、或者存在UndertowServletWebServerFactory類型的Bean。

    總結一下:

  • 有Tomcat依賴,就有TomcatServletWebServerFactory這個Bean
  • 有Jetty依賴,就有JettyServletWebServerFactory這個Bean
  • 有Undertow依賴,就有UndertowServletWebServerFactory這個Bean
  • 那么SpringBoot給我們配置的這幾個Bean到底有什么用呢?

    TomcatServletWebServerFactory

    我們前面說到,TomcatServletWebServerFactory實現了ServletWebServerFactory這個接口,這個接口的定義為:

    @FunctionalInterface public interface ServletWebServerFactory {WebServer getWebServer(ServletContextInitializer... initializers); }public interface WebServer {void start() throws WebServerException;void stop() throws WebServerException;int getPort(); }

    我們發現ServletWebServerFactory其實就是用來獲得WebServer對象的,而WebServer擁有啟動、停止、獲取端口等方法,那么很自然,我們就發現WebServer其實指的就是Tomcat、Jetty、Undertow,而TomcatServletWebServerFactory就是用來生成Tomcat所對應的WebServer對象,具體一點就是TomcatWebServer對象,并且在生成TomcatWebServer對象時會把Tomcat給啟動起來,在源碼中,調用TomcatServletWebServerFactory對象的getWebServer()方法時就會啟動Tomcat。

    我們再來看TomcatServletWebServerFactory這個Bean的定義:

    @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();factory.getTomcatConnectorCustomizers().addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatContextCustomizers().addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));factory.getTomcatProtocolHandlerCustomizers().addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));return factory; }

    要構造這個Bean,Spring會從Spring容器中獲取到TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer這三個類型的Bean,然后把它們添加到TomcatServletWebServerFactory對象中去,很明顯這三種Bean是用來配置Tomcat的,比如:

  • TomcatConnectorCustomizer:是用來配置Tomcat中的Connector組件的
  • TomcatContextCustomizer:是用來配置Tomcat中的Context組件的
  • TomcatProtocolHandlerCustomizer:是用來配置Tomcat中的ProtocolHandler組件的
  • 也就是我們可以通過定義TomcatConnectorCustomizer類型的Bean,來對Tomcat進行配置,比如:

    package com.morris.spring.boot;import org.apache.catalina.connector.Connector; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; import org.springframework.context.annotation.Bean;@SpringBootApplication public class SpringBootDemoApplication {@Beanpublic TomcatConnectorCustomizer tomcatConnectorCustomizer(){return new TomcatConnectorCustomizer() {@Overridepublic void customize(Connector connector) {connector.setPort(8888);}};}public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);}}

    這樣Tomcat就會綁定8888這個端口。

    ServletWebServerFactory的調用

    有了TomcatServletWebServerFactory這個Bean之后,在SpringBoot的啟動過程中,會執行ServletWebServerApplicationContext的onRefresh()方法,而這個方法會調用createWebServer()方法:

    private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");// 獲取TomcatServletWebServerFactoryServletWebServerFactory factory = getWebServerFactory();createWebServer.tag("factory", factory.getClass().toString());// 啟動tomcatthis.webServer = factory.getWebServer(getSelfInitializer());createWebServer.end();getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));getBeanFactory().registerSingleton("webServerStartStop",new WebServerStartStopLifecycle(this, this.webServer));}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources(); }

    很明顯,getWebServerFactory()負責獲取具體的ServletWebServerFactory對象,要么是TomcatServletWebServerFactory對象,要么是JettyServletWebServerFactory對象,要么是UndertowServletWebServerFactory對象,注意只能獲取到一個,然后調用該對象的getWebServer方法,啟動對應的Tomcat、或者Jetty、或者Undertow。

    getWebServerFactory方法中的邏輯比較簡單,獲取Spring容器中的ServletWebServerFactory類型的Bean對象,如果沒有獲取到則拋異常,如果找到多個也拋異常,也就是在Spring容器中只能有一個ServletWebServerFactory類型的Bean對象。

    拿到TomcatServletWebServerFactory對象后,就調用它的getWebServer方法,而在這個方法中就會生成一個Tomcat對象,并且利用前面的TomcatConnectorCustomizer等等會Tomcat對象進行配置,最后啟動Tomcat。

    這樣在啟動應用時就完成了Tomcat的啟動,到此我們通過這個案例也看到了具體的Starter機制、自動配置的具體使用。

    將DispatherServlet和Filter注入到Web容器

    private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {return this::selfInitialize; }private void selfInitialize(ServletContext servletContext) throws ServletException {// 將servletContext與spring容器關聯prepareWebApplicationContext(servletContext);registerApplicationScope(servletContext);// 將servletContext的初始化參數注入到spring容器中WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);for (ServletContextInitializer beans : getServletContextInitializerBeans()) {/*** DispatcherServletRegistrationBean是在DispatcherServletAutoConfiguration中注入到spring中** DispatcherServletRegistrationBean負責將DispatcherServlet注入到tomcat* FilterRegistrationBean負責將Filter注入到tomcat** @see org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean* @see org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean* @see FilterRegistrationBean*/beans.onStartup(servletContext);} }

    ServletWebServerFactoryAutoConfiguration

    我們前面提到了我們可以利用TomcatConnectorCustomizer對Tomca中的Connector組件進行配置,我們可能會想到默認情況下,SpringBoot是不是就是提供了一個TomcatConnectorCustomizer的Bean,然后給Connector配置了8080端口,或者從Environment對象中獲取server.port配置,并設置到Connector中去呢?

    并不是,因為如果SpringBoot這么實現,那么默認就得提供三個ConnectorCustomizer的Bean,一個TomcatConnectorCustomizer、一個JettyConnectorCustomizer、一個UndertowConnectorCustomizer,這是比較不恰當的,我們知道默認情況下,不管我們是用Tomcat,還是Jetty,還是Undertow,它們啟動時綁定的都是8080端口,也就是說SpringBoot并不會根據不同的WebServer設置不同的端口,也就是說SpringBoot只會給WebServer設置端口,而不會區分WebServer的不同實現。

    所以在自動配置類ServletWebServerFactoryAutoConfiguration中,會定義一個ServletWebServerFactoryCustomizer類型的Bean,定義為:

    @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())); }

    這個Bean會接收一個ServerProperties的Bean,ServerProperties的Bean對應的就是properties文件中前綴為server的配置,我們可以利用ServerProperties對象的getPort方法獲取到我們所配置的server.port的值。

    而ServletWebServerFactoryCustomizer是針對一個ServletWebServerFactory的自定義器,也就是用來配置TomcatServletWebServerFactory這個Bean的,到時候ServletWebServerFactoryCustomizer就會利用ServerProperties對象來對TomcatServletWebServerFactory對象進行設置。

    在ServletWebServerFactoryAutoConfiguration這個自動配置上,除開Import了EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow這三個配置類,還Import了一個ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,這個BeanPostProcessorsRegistrar會向Spring容器中注冊一個WebServerFactoryCustomizerBeanPostProcessor類型的Bean。

    WebServerFactoryCustomizerBeanPostProcessor是一個BeanPostProcessor,它專門用來處理類型為WebServerFactory的Bean對象,而我們的TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory也都實現了這個接口,所以不管當前項目依賴的情況,只要在Spring在創建比如TomcatServletWebServerFactory這個Bean時,WebServerFactoryCustomizerBeanPostProcessor就會對它進行處理,處理的邏輯為:

  • 從Spring容器中拿到WebServerFactoryCustomizer類型的Bean,也就是前面說的ServletWebServerFactoryCustomizer對象
  • 然后調用ServletWebServerFactoryCustomizer對象的customize方法,把TomcatServletWebServerFactory對象傳入進去
  • customize方法中就會從ServerProperties對象獲取各種配置,然后設置給TomcatServletWebServerFactory對象
  • org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor#postProcessBeforeInitialization(org.springframework.boot.web.server.WebServerFactory)

    @SuppressWarnings("unchecked") private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class).invoke((customizer) -> customizer.customize(webServerFactory)); }private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {if (this.customizers == null) {// Look up does not include the parent contextthis.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);this.customizers = Collections.unmodifiableList(this.customizers);}return this.customizers; }

    調用ServletWebServerFactoryCustomizer對象的customize方法:

    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer#customize

    @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);} }

    這樣當TomcatServletWebServerFactory這個Bean對象創建完成后,它里面的很多屬性,比如port,就已經是程序員所配置的值了,后續執行getWebServer方法時,就直接獲取自己的屬性,比如port屬性,設置給Tomcat,然后再利用TomcatConnectorCustomizer等進行處理,最后啟動Tomcat。

    到此,SpringBoot整合Tomcat的核心原理就分析完了,主要涉及的東西有:

  • spring-boot-starter-web:會自動引入Tomcat、SpringMVC的依賴
  • ServletWebServerFactoryAutoConfiguration:自動配置類
  • ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar:用來注冊WebServerFactoryCustomizerBeanPostProcessor
  • ServletWebServerFactoryConfiguration.EmbeddedTomcat:配置TomcatServletWebServerFactory
  • ServletWebServerFactoryConfiguration.EmbeddedJetty:配置JettyServletWebServerFactory
  • ServletWebServerFactoryConfiguration.EmbeddedUndertow:配置UndertowServletWebServerFactory
  • ServletWebServerFactoryCustomizer:用來配置ServletWebServerFactory
  • WebServerFactoryCustomizerBeanPostProcessor:是一個BeanPostProcessor,利用ServletWebServerFactoryCustomizer來配置ServletWebServerFactory
  • ServletWebServerApplicationContext中的onRefresh()方法:負責啟動Tomcat
  • 總結

    以上是生活随笔為你收集整理的【springboot】自动整合Tomcat原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。