您好,登錄后才能下訂單哦!
這篇文章主要介紹“SpringBoot集成tomcat的方法是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“SpringBoot集成tomcat的方法是什么”文章能幫助大家解決問題。
spring boot 支持目前主流的 servlet 容器,包括 tomcat、jetty、undertow,可以在我們的項目中方便地集成這些 servlet 容器,減少了開發(fā)、運維的工作量。而傳統(tǒng)的應用開發(fā),需要經(jīng)過繁鎖的操作步驟:安裝 tomcat –> 修改 tomcat 配置 –> 部署 war 包 –> 啟動 tomcat –> 運維……,這個工作量不小,尤其是集群部署、應用遷移的時候。而采用 spring boot 之后,一切變得如此簡單,打包 –> java -jar –> 運維,只需要一個 jar 包便可以隨意部署安裝。
在分析源碼前,我們先來了解下 spring 的 SPI 機制。我們知道,jdk 為了方便應用程序進行擴展,提供了默認的 SPI 實現(xiàn)(ServiceLoader),dubbo 也有自己的 SPI。spring 也是如此,他為我們提供了SpringFactoriesLoader
,允許開發(fā)人員通過META-INF/spring.factories
文件進行擴展,下面舉一個例子方便理解
假如,我想要往 spring 容器中添加一個ApplicationContextInitializer
做一些初始化工作,我們可以借助 spring 提供的這個 SPI 功能完成這個需求。
首先,在項目中創(chuàng)建META-INF/spring.factories
文件,文件內(nèi)容如下所示:
org.springframework.context.ApplicationContextInitializer=\
我們再寫個 test case,便可以通過 SPI 的方式獲取我們定義的ApplicationContextInitializer
??此坪芎唵蔚囊粋€功能,但是 spring boot 正是利用這個強大的擴展點,在 spring framework 的基礎上為我們集成了常用的開源框架
@Test public void testSpringSpi() { List<ApplicationListener> listeners = SpringFactoriesLoader.loadFactories( ApplicationListener.class, ClassUtils.getDefaultClassLoader() ); System.out.println( listeners );
我們再來看看這個SpringFactoriesLoader
,關鍵代碼如下所示,它通過讀取META-INF/spring.factories
文件,并且查找方法參數(shù)指定的 class,然后創(chuàng)建對應的實例對象,并且返回。此外,還支持排序,可以使用以下幾種方式進行排序
org.springframework.core.Ordered:實現(xiàn)該接口
org.springframework.core.annotation.Order:注解
javax.annotation.Priority:注解
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) { List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); List<T> result = new ArrayList<T>(factoryNames.size()); for (String factoryName : factoryNames) { result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result;
接下來,我們來分析下 spring boot 是如何利用 SPI 機制集成 tomcat
在分析 tomcat 集成的源碼之前,我們先來了解下 EmbeddedServletContainer
EmbeddedServletContainer:
spring 用EmbeddedServletContainer
封裝了內(nèi)嵌的 servlet 容器,提供了start
、stop
等接口用于控制容器的生命周期,并且 spring 內(nèi)置了 tomcat、jetty、undertow 容器的實現(xiàn),類圖所下所示
我們再來看看 spring boot 中最常用的SpringBootApplication
注解,原來是多個注解的綜合體,而這個EnableAutoConfiguration
便是 spring boot 用做自動化配置的注解
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { // code......
我們在spring-boot-autoconfigure
模塊可以看到大量的 SPI 配置,部分如下所示
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
原來EnableAutoConfiguration
注解引入了EmbeddedServletContainerAutoConfiguration
,而這個便是內(nèi)嵌 servlet 容器的配置類,tomcat、jetty、undertow 都在這個類上面,通過@ConditionalOnClass
注解加載不同的 servlet 容器。但是,這個類僅僅是注冊了TomcatEmbeddedServletContainerFactory
,不足以幫助我們解除所有的困惑。不要急,我們先來看看TomcatEmbeddedServletContainerFactory
的類圖。
由上面的類圖可知,它實現(xiàn)了以下接口:
EmbeddedServletContainerFactory:它是一個工廠模式,用于創(chuàng)建EmbeddedServletContainer
,即用于創(chuàng)建一個內(nèi)嵌的 Servlet 容器,這個接口里面只有一個getEmbeddedServletContainer
方法
ConfigurableEmbeddedServletContainer:用于配置EmbeddedServletContainer
,比如說端口、上下文路徑等
分析了上面兩個接口,原來創(chuàng)建 servlet 容器的工作是由EmbeddedServletContainerFactory
完成的,看下getEmbeddedServletContainer
方法的調(diào)用棧。在EmbeddedWebApplicationContext
中重寫了GenericWebApplicationContext#onRefresh()
方法,并且調(diào)用getEmbeddedServletContainer
方法創(chuàng)建 servlet 容器,我們接下來分析這個創(chuàng)建過程。
關鍵代碼如下(省略異常處理):
EmbeddedWebApplicationContext.java @Override protected void onRefresh() { super.onRefresh(); createEmbeddedServletContainer(); } private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServletContext(); if (localContainer == null && localServletContext == null) { // 從容器中獲取bean,如果使用tomcat則返回TomcatEmbeddedServletContainerFactory EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory(); this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer()); } else if (localServletContext != null) { getSelfInitializer().onStartup(localServletContext); } initPropertySources();
我們先畫出主要的流程圖
由上圖可知,EmbeddedWebApplicationContext
在執(zhí)行onRefresh
方法的時候,首先調(diào)用父類的onRefresh
,然后從容器中獲取EmbeddedServletContainerFactory
的實現(xiàn)類。由于我們在 classpath 下面可以獲取 tomcat 的 jar 包,因此EmbeddedServletContainerAutoConfiguration
會在 spring 容器中注冊TomcatEmbeddedServletContainerFactory
這個 bean。然后,由它創(chuàng)建TomcatEmbeddedServletContainer
,我們來看看具體的創(chuàng)建過程,代碼如下所示:
TomcatEmbeddedServletContainerFactory.java @Override public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) { Tomcat tomcat = new Tomcat(); // 實例化 apache Tomcat File baseDir = (this.baseDirectory != null ? this.baseDirectory : createTempDir("tomcat")); tomcat.setBaseDir(baseDir.getAbsolutePath()); // 創(chuàng)建 Connector 組件,默認使用org.apache.coyote.http11.Http11NioProtocol Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); // 支持對 Connector 進行自定義設置,比如設置線程池、最大連接數(shù)等 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); return getTomcatEmbeddedServletContainer(tomcat);
首先是實例化Tomcat
對象,然后創(chuàng)建Connector
組件,并且對Connector
進行相關的參數(shù)設置,同時也允許我們通過TomcatConnectorCustomizer
接口進行自定義的設置。OK,創(chuàng)建了Tomcat
實例之后,需要創(chuàng)建TomcatEmbeddedServletContainer
,它依賴Tomcat
對象,在構造方法中便會啟動 Tomcat 容器,從而完成各個組件的啟動流程
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); } private void initialize() throws EmbeddedServletContainerException { synchronized (this.monitor) { addInstanceIdToEngineName(); // Remove service connectors to that protocol binding doesn't happen yet removeServiceConnectors(); // Start the server to trigger initialization listeners this.tomcat.start(); // We can re-throw failure exception directly in the main thread rethrowDeferredStartupExceptions(); Context context = findContext(); ContextBindings.bindClassLoader(context, getNamingToken(context), getClass().getClassLoader()); // Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown startDaemonAwaitThread(); }
關于“SpringBoot集成tomcat的方法是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識,可以關注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。