欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot集成tomcat詳解實(shí)現(xiàn)過(guò)程

 更新時(shí)間:2023年02月23日 08:54:25   作者:Java面試大全  
采用spring boot之后,一切變得如此簡(jiǎn)單,打包->java-jar->運(yùn)維,只需要一個(gè)jar包便可以隨意部署安裝。這篇文章,將對(duì) spring boot集成tomcat的源碼進(jìn)行分析,探索其內(nèi)部的原理

spring boot 支持目前主流的 servlet 容器,包括 tomcat、jetty、undertow,可以在我們的項(xiàng)目中方便地集成這些 servlet 容器,減少了開(kāi)發(fā)、運(yùn)維的工作量。而傳統(tǒng)的應(yīng)用開(kāi)發(fā),需要經(jīng)過(guò)繁鎖的操作步驟:安裝 tomcat –> 修改 tomcat 配置 –> 部署 war 包 –> 啟動(dòng) tomcat –> 運(yùn)維……,這個(gè)工作量不小,尤其是集群部署、應(yīng)用遷移的時(shí)候。而采用 spring boot 之后,一切變得如此簡(jiǎn)單,打包 –> java -jar –> 運(yùn)維,只需要一個(gè) jar 包便可以隨意部署安裝。這篇文章,將對(duì) spring boot 集成 tomcat 的源碼進(jìn)行分析,探索其內(nèi)部的原理

SPI

在分析源碼前,我們先來(lái)了解下 spring 的 SPI 機(jī)制。我們知道,jdk 為了方便應(yīng)用程序進(jìn)行擴(kuò)展,提供了默認(rèn)的 SPI 實(shí)現(xiàn)(ServiceLoader),dubbo 也有自己的 SPI。spring 也是如此,他為我們提供了SpringFactoriesLoader,允許開(kāi)發(fā)人員通過(guò)META-INF/spring.factories文件進(jìn)行擴(kuò)展,下面舉一個(gè)例子方便理解

假如,我想要往 spring 容器中添加一個(gè)ApplicationContextInitializer做一些初始化工作,我們可以借助 spring 提供的這個(gè) SPI 功能完成這個(gè)需求。

首先,在項(xiàng)目中創(chuàng)建META-INF/spring.factories文件,文件內(nèi)容如下所示:

org.springframework.context.ApplicationContextInitializer=\

我們?cè)賹?xiě)個(gè) test case,便可以通過(guò) SPI 的方式獲取我們定義的ApplicationContextInitializer??此坪芎?jiǎn)單的一個(gè)功能,但是 spring boot 正是利用這個(gè)強(qiáng)大的擴(kuò)展點(diǎn),在 spring framework 的基礎(chǔ)上為我們集成了常用的開(kāi)源框架

@Test
public void testSpringSpi() {
    List<ApplicationListener> listeners = SpringFactoriesLoader.loadFactories( ApplicationListener.class, 
            ClassUtils.getDefaultClassLoader() );
    System.out.println( listeners );

我們?cè)賮?lái)看看這個(gè)SpringFactoriesLoader,關(guān)鍵代碼如下所示,它通過(guò)讀取META-INF/spring.factories文件,并且查找方法參數(shù)指定的 class,然后創(chuàng)建對(duì)應(yīng)的實(shí)例對(duì)象,并且返回。此外,還支持排序,可以使用以下幾種方式進(jìn)行排序

  • org.springframework.core.Ordered:實(shí)現(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;

接下來(lái),我們來(lái)分析下 spring boot 是如何利用 SPI 機(jī)制集成 tomcat

SpringBoot for Tomcat

在分析 tomcat 集成的源碼之前,我們先來(lái)了解下 EmbeddedServletContainer

EmbeddedServletContainer:

spring 用EmbeddedServletContainer封裝了內(nèi)嵌的 servlet 容器,提供了start、stop等接口用于控制容器的生命周期,并且 spring 內(nèi)置了 tomcat、jetty、undertow 容器的實(shí)現(xiàn),類圖所下所示

我們?cè)賮?lái)看看 spring boot 中最常用的SpringBootApplication注解,原來(lái)是多個(gè)注解的綜合體,而這個(gè)EnableAutoConfiguration便是 spring boot 用做自動(dòng)化配置的注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    // code......

我們?cè)?code>spring-boot-autoconfigure模塊可以看到大量的 SPI 配置,部分如下所示

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\

原來(lái)EnableAutoConfiguration注解引入了EmbeddedServletContainerAutoConfiguration,而這個(gè)便是內(nèi)嵌 servlet 容器的配置類,tomcat、jetty、undertow 都在這個(gè)類上面,通過(guò)@ConditionalOnClass注解加載不同的 servlet 容器。但是,這個(gè)類僅僅是注冊(cè)了TomcatEmbeddedServletContainerFactory,不足以幫助我們解除所有的困惑。不要急,我們先來(lái)看看TomcatEmbeddedServletContainerFactory的類圖。

由上面的類圖可知,它實(shí)現(xiàn)了以下接口:

  • EmbeddedServletContainerFactory:它是一個(gè)工廠模式,用于創(chuàng)建EmbeddedServletContainer,即用于創(chuàng)建一個(gè)內(nèi)嵌的 Servlet 容器,這個(gè)接口里面只有一個(gè)getEmbeddedServletContainer方法
  • ConfigurableEmbeddedServletContainer:用于配置EmbeddedServletContainer,比如說(shuō)端口、上下文路徑等

分析了上面兩個(gè)接口,原來(lái)創(chuàng)建 servlet 容器的工作是由EmbeddedServletContainerFactory完成的,看下getEmbeddedServletContainer方法的調(diào)用棧。在EmbeddedWebApplicationContext中重寫(xiě)了GenericWebApplicationContext#onRefresh()方法,并且調(diào)用getEmbeddedServletContainer方法創(chuàng)建 servlet 容器,我們接下來(lái)分析這個(gè)創(chuàng)建過(guò)程。

關(guān)鍵代碼如下(省略異常處理):

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

我們先畫(huà)出主要的流程圖

由上圖可知,EmbeddedWebApplicationContext在執(zhí)行onRefresh方法的時(shí)候,首先調(diào)用父類的onRefresh,然后從容器中獲取EmbeddedServletContainerFactory的實(shí)現(xiàn)類。由于我們?cè)?classpath 下面可以獲取 tomcat 的 jar 包,因此EmbeddedServletContainerAutoConfiguration會(huì)在 spring 容器中注冊(cè)TomcatEmbeddedServletContainerFactory這個(gè) bean。然后,由它創(chuàng)建TomcatEmbeddedServletContainer,我們來(lái)看看具體的創(chuàng)建過(guò)程,代碼如下所示:

TomcatEmbeddedServletContainerFactory.java
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
        ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();   // 實(shí)例化 apache Tomcat 
    File baseDir = (this.baseDirectory != null ? this.baseDirectory
            : createTempDir("tomcat"));
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // 創(chuàng)建 Connector 組件,默認(rèn)使用org.apache.coyote.http11.Http11NioProtocol
    Connector connector = new Connector(this.protocol); 
    tomcat.getService().addConnector(connector);
    // 支持對(duì) Connector 進(jìn)行自定義設(shè)置,比如設(shè)置線程池、最大連接數(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);

首先是實(shí)例化Tomcat對(duì)象,然后創(chuàng)建Connector組件,并且對(duì)Connector進(jìn)行相關(guān)的參數(shù)設(shè)置,同時(shí)也允許我們通過(guò)TomcatConnectorCustomizer接口進(jìn)行自定義的設(shè)置。OK,創(chuàng)建了Tomcat實(shí)例之后,需要?jiǎng)?chuàng)建TomcatEmbeddedServletContainer,它依賴Tomcat對(duì)象,在構(gòu)造方法中便會(huì)啟動(dòng) Tomcat 容器,從而完成各個(gè)組件的啟動(dòng)流程

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

到此這篇關(guān)于SpringBoot集成tomcat詳解實(shí)現(xiàn)過(guò)程的文章就介紹到這了,更多相關(guān)SpringBoot集成tomcat內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論