天天看點

SpringBoot——嵌入式Servlet容器自動配置原理以及啟動原理0 嵌入式Servlet容器自動配置原理以及啟動原理的步驟1 嵌入式Servlet容器自動配置原理(以Tomcat為例)2 嵌入式Servlet容器啟動原理(以Tomcat為例)

  • 0 嵌入式Servlet容器自動配置原理以及啟動原理的步驟
  • 1 嵌入式Servlet容器自動配置原理(以Tomcat為例)
    • 1.1 簡單介紹
  • 2 嵌入式Servlet容器啟動原理(以Tomcat為例)
    • 2.1 SpringBoot啟動時,啟動Tomcat的原理
    • 2.2 嵌入式的Servlet容器工廠建立tomcat容器,初始化并啟動容器
      • 2.2.1 嵌入式Servlet容器工廠的自動配置類
        • 1> 注冊後置處理器(WebServerFactoryCustomizerBeanPostProcessor)
        • 2> 後置處理器(WebServerFactoryCustomizerBeanPostProcessor)
        • 3> web服務工廠定制器(WebServerFactoryCustomizer)
      • 2.2.2 嵌入式的Servlet容器工廠的配置類

版本說明:

  • Spring Boot 2.x 版本:嵌入式Servlet容器自動配置是通過

    WebServerFactoryCustomizer

    定制器 來定制的;
  • Spring Boot 1.x 版本:是通過

    EmbeddedServletContainerCustomizer

    嵌入式的Servlet容器定制器來定制的。

SpringBoot官方文檔:

Embedded Web Servers、

Embedded Servlet Container Support

參考部落格:https://cloud.tencent.com/developer/article/1551867

0 嵌入式Servlet容器自動配置原理以及啟動原理的步驟

步驟:

  • SpringBoot 啟動時,根據導入的依賴資訊,自動建立對應的

    WebServerFactoryCustomizer

    (web服務工廠定制器);
  • WebServerFactoryCustomizerBeanPostProcessor

    (web服務工廠定制器元件的後置處理器)擷取所有類型為web服務工廠定制器的元件(包含實作WebServerFactoryCustomizer接口,自定義的定制器元件),依次調用

    customize

    方法,定制Servlet容器配置;
  • 嵌入式的Servlet容器工廠建立Tomcat容器,初始化并啟動容器。

1 嵌入式Servlet容器自動配置原理(以Tomcat為例)

1.1 簡單介紹

SpringBoot預設使用

Tomcat

作為嵌入式的Servlet容器,

這裡所謂的嵌入式伺服器就是,SpringBoot使用 apache組織 提供的Tomcat的API來配置和定制Tomcat服務。

SpringBoot在啟應用動時都會加載各個jar包下的

/META-INF/spring.factories

檔案,讀取其中的

org.springframework.boot.autoconfigure.EnableAutoConfiguration

類型的所有自動配置類,進而達到自動配置的效果。

而我們的servlet容器的自動配置也是從這個配置檔案着手,在

spring-boot-autoconfigure-2.3.1.RELEASE.jar

包下的

/META-INF/spring.factories

配置檔案中,有一個自動配置類,是用于Servlet容器的自動配置的。

// ServletWebServerFactoryAutoConfiguration 就是嵌入式servlet容器的自動配置類
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
           

ServletWebServerFactoryAutoConfiguration

是嵌入式Servlet容器的自動配置類,這個類的主要作用是

建立

TomcatServletWebServerFactory

工廠類,

建立

TomcatServletWebServerFactoryCustomizer

定制器類,

建立

FilterRegistrationBean

注冊

ForwardedHeaderFilter

(ForwardedHeaderFilter會從Forwarded,X-Forwarded-Host,X-Forwarded-Port或者X-Forwarded-Proto中擷取跳轉資訊),

同時很關鍵的一步是注冊後置處理器

webServerFactoryCustomizerBeanPostProcessor

隻要

Application

類一啟動,就會執行

run

方法,

run經過一系列調用會通過

ServletWebServerApplicationContext

onRefresh

方法建立ioc容器,

然後通過

createWebServer

方法,createWebServer方法會去ioc容器裡掃描是否有對應的

ServletWebServerFactory

工廠類(

TomcatServletWebServerFactory

是其中一種),

掃描得到,就會觸發

webServerFactoryCustomizerBeanPostProcessor

後置處理器類,

這個處理器類會擷取

TomcatServletWebServerFactoryCustomizer

定制器,并調用

customize

方法進行定制,

這時候工廠類起作用,調用

getWebServer

方法進行Tomcat屬性配置和引擎設定等等,

再建立TomcatWebServer啟動Tomcat容器。

2 嵌入式Servlet容器啟動原理(以Tomcat為例)

2.1 SpringBoot啟動時,啟動Tomcat的原理

主要 是 圍繞

ServletWebServerApplicationContext

類進行

(1)我們啟動springboot應用時,都是直接運作主程式的

main

方法,然後調用裡面的

SpringApplication

類的

run

方法。

(2)調用run方法時,會調用該類自己的

refreshContext

方法,這個方法的作用是建立IOC容器對象,并且初始化IOC容器,建立容器中的每一個元件。

(3)在該類的

reflesh

方法中,再調用了

ServletWebServerApplicationContext

類的

reflesh

方法,重新整理剛才的IOC容器後(這個方法其實是繼承

AbstractApplicationContext

類的),在該方法中又調用

onreflesh

方法(這個方法則是

ServletWebServerApplicationContext

類重寫

AbstractApplicationContext

類的),最後在

onreflesh

中調用

createWebServer

方法,建立 WebServer對象。

(4)

createWebServer

方法,該方法最終能夠擷取到一個與目前應用(根據我們導入的依賴來擷取)所導入的Servlet類型相比對的web服務工廠

TomcatServletWebServerFactory

,通過該工廠就可以擷取到相應的

WebServerFactoryCustomizer

(Web服務工廠定制器),在調用該工廠的

getWebServer

方法,建立->初始化->啟動Tomcat。

注:

createWebServer

方法執行期間,我們其實會來到

EmbeddedWebServerFactoryCustomizerAutoConfiguration

,然後根據條件(配置的依賴)配置哪一個Web伺服器。

* 在SpringBoot的run啟動時,會判斷目前所處環境。

* 如果是Web環境則通過建立一個`ServletWebServerApplicationContext`,執行構造函數的refresh方法,

* 在refresh方法内執行onRefresh方法,執行createWebServer方法,這個方法會根據目前應用是内置還是外置釋出方式來決定以何種方式擷取web伺服器。

* 如果是内置方式則通過`TomcatServletWebServerFactory`工廠類來擷取一個首選的web伺服器,然後進行伺服器的初始化配置,應用加載生效以及伺服器啟動的操作。
           

2.2 嵌入式的Servlet容器工廠建立tomcat容器,初始化并啟動容器

2.2.1 嵌入式Servlet容器工廠的自動配置類

ServletWebServerFactoryAutoConfiguration

// 标注這是一個配置類
@Configuration(
    proxyBeanMethods = false
)
// 決定配置類的加載順序的,當注解裡的值越小越先加載
@AutoConfigureOrder(-2147483648)
// 表示目前必須有 servlet-api 依賴存在
@ConditionalOnClass({ServletRequest.class})
// 僅僅基于Servlet的web應用程式
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
// 向IOC容器中添加ServerProperties元件
@EnableConfigurationProperties({ServerProperties.class})
// 向IOC容器中注入一些元件
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,  
            EmbeddedTomcat.class, 
            EmbeddedJetty.class, 
            EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    //略
}
           

@Import

注解可以看到,這個配置類向容器中批量添加元件,支援添加三種web伺服器(Tomcat、Jetty、Undertow),而具體要配置哪個伺服器,由

ServletWebServerFactoryConfiguration

決定,同時還定義了配置的順序Tomcat>Jetty>Undertow,意思就是當環境中有Tomcat滿足的依賴時就會優先使用Tomcat,依次往後推。

綜合來看,

ServletWebServerFactoryConfiguration

自動配置類作了以下幾件事:

  • 向IOC容器中添加了内部類

    BeanPostProcessorsRegistrar

    這個bean後置處理器注冊器元件,這個元件也是向IOC容器中添加元件後置處理器,這裡添加的是

    WebServerFactoryCustomizerBeanPostProcessor

    伺服器工廠的定制器的後置處理器 ,對于

    xxxBeanPostProcessor

    來說,都是bean的後置處理器,即在bean建立完成後,在其初始化前後進行攔截處理。注冊的後置處理器類

    WebServerFactoryCustomizerBeanPostProcessor

    ,在ioc容器啟動的時候會調用。
  • 向IOC容器中添加了

    ServletWebServerFactoryConfiguration.EmbeddedTomcat

    等嵌入容器相關配置(這裡以 tomcat 相關的配置為例)。
  • 向IOC容器中添加了

    ServletWebServerFactoryCustomizer

    TomcatServletWebServerFactoryCustomizer

    兩個

    WebServerFactoryCustomizer

    類型的 bean。

1> 注冊後置處理器(BeanPostProcessorsRegistrar)

部分代碼

// ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    // 略

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    if (this.beanFactory != null) {
            // 注冊 webServerFactoryCustomizerBeanPostProcessor
            this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
            // 注冊 errorPageRegistrarBeanPostProcessor
            this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
        }
    }

    // 略
}
           

BeanPostProcessorsRegistar

注冊了兩個 bean,一個

WebServerFactoryCustomizerBeanPostProcessor

,一個

errorPageRegistrarBeanPostProcessor

;這兩個都實作類

BeanPostProcessor

接口,屬于 bean 的後置處理器,作用是在 bean 初始化前後加一些自己的邏輯處理。

  • WebServerFactoryCustomizerBeanPostProcessor

    :作用是在

    WebServerFactory

    初始化時調用上面自動配置類注入的那些

    WebServerFactoryCustomizer

    ,然後調用 WebServerFactoryCustomizer 中的

    customize

    方法來 處理 WebServerFactory。
  • errorPageRegistrarBeanPostProcessor:和上面的作用差不多,不過這個是處理 ErrorPageRegistrar 的。

2> 後置處理器(WebServerFactoryCustomizerBeanPostProcessor)

這個後置處理器,拿到所有WebServerFactoryCustomizer,分别執行對應的customize方法,定制Servlet容器,自定義的WebServerFactoryCustomizer是最後執行的,是以可以覆寫之前的預設的配置,最後 綁定到 工廠類(WebServerFactory)。

部分代碼

// org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    // 初始化 bean 之前調用
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 判斷這個bean 是不是 繼承自 WebServerFactory
        if (bean instanceof WebServerFactory) {
            // 是則執行
            this.postProcessBeforeInitialization((WebServerFactory)bean);
        }
        return bean;
    }
    
    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        // 周遊WebServerFactory 中所有 WebServerFactoryCustomizer 類,執行對應的customize方法,定制Servlet容器
        ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
            customizer.customize(webServerFactory);
        });
    }

    // 初始化 bean 之後調用
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
           

3> web服務工廠定制器(WebServerFactoryCustomizer)

TomcatWebServerFactoryCustomizer

ServletWebServerFactoryCustomizer

為例

這兩個 Customizer 實際上就是去處理一些配置值,然後綁定到 各自的工廠類中,将

ServerProperties

配置值綁定給

ConfigurableServletWebServerFactory

對象執行個體上

TomcatWebServerFactoryCustomizer

部分代碼

public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
    // 略

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        // 拿到 tomcat 相關的配置
        ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
        // server.tomcat.additional-tld-skip-patterns
        if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
            factory.getTldSkipPatterns()
                .addAll(tomcatProperties.getAdditionalTldSkipPatterns());
        }
        // server.redirectContextRoot
        if (tomcatProperties.getRedirectContextRoot() != null) {
            customizeRedirectContextRoot(factory,
                                        tomcatProperties.getRedirectContextRoot());
        }
        // server.useRelativeRedirects
        if (tomcatProperties.getUseRelativeRedirects() != null) {
            customizeUseRelativeRedirects(factory,
                                        tomcatProperties.getUseRelativeRedirects());
        }
        // 同上,略
    }
    // 略
}
           

ServletWebServerFactoryCustomizer

部分代碼

public class ServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
    // 略

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
        // 端口
        map.from(this.serverProperties::getPort).to(factory::setPort);
        // address
        map.from(this.serverProperties::getAddress).to(factory::setAddress);
        // contextPath
        map.from(this.serverProperties.getServlet()::getContextPath)
            .to(factory::setContextPath);
        // displayName
        map.from(this.serverProperties.getServlet()::getApplicationDisplayName)
            .to(factory::setDisplayName);
        // session 配置
        map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
        // ssl
        map.from(this.serverProperties::getSsl).to(factory::setSsl);
        // jsp
        map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
        // 壓縮配置政策實作
        map.from(this.serverProperties::getCompression).to(factory::setCompression);
        // http2 
        map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
        // serverHeader
        map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
        // contextParameters
        map.from(this.serverProperties.getServlet()::getContextParameters)
            .to(factory::setInitParameters);
        // 同上,略
    }
    //略
}
           

2.2.2 嵌入式的Servlet容器工廠的配置類

ServletWebServerFactoryConfiguration

,分别對上述支援的三種servlet容器進行了配置。
@Configuration(
    proxyBeanMethods = false
)
class ServletWebServerFactoryConfiguration {
    // 略 三個内部類
}
           

可以發現這個類中有三個内部類,而這三個内部類也是具體的Servlet容器工廠配置類,分别通過

@ConditionalOnClass

@ConditionalOnMissingBean

限制配置類是否生效(判斷是否存在自己手動添加的ServletWebServerFactory),由此可見,要想切換servlet容器,隻需要添加相應容器的依賴,排除其他容器依賴即可。

以嵌入式的Tomcat 伺服器為例,判斷環境中是否引入了Tomcat所需的依賴

Servlet.class

,

Tomcat.class

,

UpgradeProtocol.class

,同時使用者沒有自己進行Web伺服器配置(比如自己通過實作

ServletWebServerFactory

接口進行手動配置web伺服器),這個工廠類内部就會對Tomcat進行一些初始化操作,那麼這個Tomcat伺服器就會生效。

// 嵌入式的Tomcat配置類
@Configuration(
    proxyBeanMethods = false
)
// 判斷環境中是否引入了Tomcat所需的依賴`Servlet.class`, `Tomcat.class`, `UpgradeProtocol.class`
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
// 使用者沒有自己實作`ServletWebServerFactory`接口,配置Web服務,加入IOC容器
@ConditionalOnMissingBean(
    value = {ServletWebServerFactory.class},
    search = SearchStrategy.CURRENT
)
static class EmbeddedTomcat {
    EmbeddedTomcat() {
    }
    // 通過@Bean向容器中添加了TomcatServletWebServerFactory元件
    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
        return factory;
    }
}
           

可以知道這是 TomcatServletWebServer 容器工廠,

TomcatServletWebServerFactory

有一個

getWebServer

方法獲得TomcatWebServer,對Tomcat進行的一些初始化操作,最重要的操作都在

getWebServer

方法内。

  • getWebServer

    方法:
    • 起初先建立一個tomcat,在設定一些配置,再通過

      getTomcatWebServer(tomcat)

      獲得TomcatWebServer。
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    // 建立 Tomcat
    // 用的是最底層的tomcat執行個體進行配置(通過new Tomcat的方式,而這個Tomcat是tomcat源碼包的一個執行個體類 package org.apache.catalina.startup)
    // 主要對端口,協定,tomcat元件對象等進行初始化并封裝
    Tomcat tomcat = new Tomcat();
    // 以下是對 Tomcat 的一些設定
    File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    this.customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    this.configureEngine(tomcat.getEngine());
    Iterator var5 = this.additionalTomcatConnectors.iterator();

    while(var5.hasNext()) {
        Connector additionalConnector = (Connector)var5.next();
        tomcat.getService().addConnector(additionalConnector);
    }
    // 将要釋出的Web應用資訊Context初始化到tomcat中
    this.prepareContext(tomcat.getHost(), initializers);
    // 獲得 TomcatWebServer
    return this.getTomcatWebServer(tomcat);
}
           

進入到

getTomcatWebServer(tomcat)

方法中

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    // 對 tomcat 的初始化,進行封裝和啟動
    // 最後将這個tomcat對象封裝為一個TomcatWebServer對象供SpringBoot啟動時調用
    return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
}
           

進入

TomcatWebServer

類中

// 對 tomcat 的初始化,進行封裝和啟動
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    this.monitor = new Object();
    this.serviceConnectors = new HashMap();
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
    // 初始化 Tomcat
    this.initialize();
}

// 初始化 tomcat ,并啟動
private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
    synchronized(this.monitor) {
        try {
            this.addInstanceIdToEngineName();
            Context context = this.findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                    this.removeServiceConnectors();
                }

            });
            // 對初始化好的 tomcat 進行啟動
            this.tomcat.start();
            this.rethrowDeferredStartupExceptions();

            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader());
            } catch (NamingException var5) {
            }

            this.startDaemonAwaitThread();
        } catch (Exception var6) {
            this.stopSilently();
            this.destroySilently();
            throw new WebServerException("Unable to start embedded Tomcat", var6);
        }

    }
}
           

綜上所述,Servlet容器的啟動原理,實際上是SpringBoot通過建立原生Tomcat對象,對這個對象進行端口,協定,元件等初始化,并且将Web應用資訊Context對象封裝到這個 tomcat對象中,然後,在Web應用資訊配置生命周期監聽生效後,啟動tomcat,最後将這個過程封裝到一個WebServer對象中供SpringBoot啟動時調用。