首頁 >Java >java教程 >SpringBoot整合tomcat的方法是什麼

SpringBoot整合tomcat的方法是什麼

王林
王林轉載
2023-05-14 19:43:141145瀏覽

spring boot 支援目前主流的 servlet 容器,包括 tomcat、jetty、undertow,可以在我們的專案中方便地整合這些 servlet 容器,減少了開發、運維的工作量。而傳統的應用開發,需要經過繁鎖的操作步驟:安裝tomcat –> 修改tomcat 設定–> 部署war 套件–> 啟動tomcat –> 運維……,這個工作工作量不小,尤其是叢集部署、應用遷移的時候。而採用 spring boot 之後,一切變得如此簡單,打包 –> java -jar –> 運維,只需要一個 jar 包便可以隨意部署安裝。

SPI

在分析原始碼前,我們先來了解下 spring 的 SPI 機制。我們知道,jdk 為了方便應用程式進行擴展,提供了預設的 SPI 實作(ServiceLoader),dubbo 也有自己的 SPI。 spring 也是如此,他為我們提供了SpringFactoriesLoader,允許開發人員透過META-INF/spring.factories檔案進行擴展,下面舉一個例子方便理解

#假如,我想要在spring 容器中加入一個ApplicationContextInitializer做一些初始化工作,我們可以藉助spring 提供的這個SPI 功能完成這個需求。

首先,在專案中創建META-INF/spring.factories文件,文件內容如下所示:

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文件,並且尋找方法參數指定的class,然後建立對應的實例對象,並且傳回。此外,也支援排序,可以使用以下幾種方式進行排序

  • org.springframework.core.Ordered:實作該介面

  • 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

SpringBoot for Tomcat

在分析tomcat 整合的原始碼之前,我們先來了解下EmbeddedServletContainer

EmbeddedServletContainer:

spring 以EmbeddedServletContainer封裝了內嵌的servlet 容器,提供了startstop等介面用於控制容器的生命週期,並且spring 內建了tomcat 、jetty、undertow 容器的實現,類別圖所下所示

我們再來看看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.EmbeddedServletContainerAuto#Configuration,\\autoconfigure.web.EmbeddedServletContainerAuto

,\\\\
# #原來

EnableAutoConfiguration註解引入了EmbeddedServletContainerAutoConfiguration,而這個便是內嵌servlet 容器的設定類,tomcat、jetty、undertow 都在這個類別上面,透過@ConditionalOnClass註解載入不同的servlet 容器。但是,這個類別只是註冊了TomcatEmbeddedServletContainerFactory,不足以幫助我們解除所有的困惑。不要著急,我們先來看看TomcatEmbeddedServletContainerFactory的類別圖。

由上面的類別圖可知,它實作了以下介面:

  • EmbeddedServletContainerFactory:它是一個工廠模式,用於建立

    EmbeddedServletContainer,即用於建立一個內嵌的Servlet 容器,這個介面裡面只有一個getEmbeddedServletContainer方法

  • ConfigurableEmbeddedServletContainer:用於設定

    EmbeddedServletContainer

##說端口、上下文路徑等分析了上面兩個接口,原來創建servlet 容器的工作是由

EmbeddedServletContainerFactory

完成的,看下

getEmbeddedServletContainer

方法的呼叫堆疊。在

EmbeddedWebApplicationContext###中重寫了###GenericWebApplicationContext#onRefresh()###方法,並且呼叫###getEmbeddedServletContainer###方法建立 servlet 容器,我們接下來分析這個建立過程。 ######關鍵程式碼如下(省略異常處理):###
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();
###我們先畫出主要的流程圖###

SpringBoot整合tomcat的方法是什麼

由上图可知,EmbeddedWebApplicationContext在执行onRefresh方法的时候,首先调用父类的onRefresh,然后从容器中获取EmbeddedServletContainerFactory的实现类。由于我们在 classpath 下面可以获取 tomcat 的 jar 包,因此EmbeddedServletContainerAutoConfiguration会在 spring 容器中注册TomcatEmbeddedServletContainerFactory这个 bean。然后,由它创建TomcatEmbeddedServletContainer,我们来看看具体的创建过程,代码如下所示:

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());
    // 创建 Connector 组件,默认使用org.apache.coyote.http11.Http11NioProtocol
    Connector connector = new Connector(this.protocol); 
    tomcat.getService().addConnector(connector);
    // 支持对 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);
    return getTomcatEmbeddedServletContainer(tomcat);

首先是实例化Tomcat对象,然后创建Connector组件,并且对Connector进行相关的参数设置,同时也允许我们通过TomcatConnectorCustomizer接口进行自定义的设置。OK,创建了Tomcat实例之后,需要创建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&#39;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的方法是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除