天天看點

Spring容器重新整理—09—onRefresh

作者:當年三歲半
Spring容器重新整理—09—onRefresh

這次的内容是上圖中的第09步。

前言

先來看看前面幾篇文章都做了什麼:

`01-prepareRefresh()`: `earlyApplicationListeners`、`applicationListeners` 、`initPropertySources()`、`validateRequiredProperties`
`02-obtainFreshBeanFactory()`: `refreshBeanFactory`
`03-prepareBeanFactory()`: `ignoreDependencyInterface`、`setBeanExpressionResolver`、`registerResolvableDependency`、`Environment`
`04-postProcessBeanFactory()`: 具體實作類的特殊邏輯(`Servlet` 等)
`05-invokeBeanFactoryPostProcessors()`: 可能發生的對 `BeanFactory/BeanDefinitionRegistry` 的修改操作
`06-registerBeanPostProcessors()`: 所有的 `BeanPostProcessor` 已經被執行個體化并且注冊到了 `BeanFactory` 中
`07-initMessageSource()`: `MessageSource` 已經就緒了,可以支援國際化(`I18N`)資源處理了
`08-initApplicationEventMulticaster()`: 多點傳播器 `applicationEventMulticaster` 已經就緒了           

目前為止,ApplicationContext 已經初始化的差不多了。

但是ApplicationContext 的實作類實在是太多了,在建立剩下的單例 Bean 之前,還給子類留了一個擴充方法: onRefresh()。

ApplicationContext 的不同實作類可以針對不同環境做一些不同的事情。

最常見的就是:web 環境下可以在這一步去建立 webServer (比如 Netty/Tomcat) 了。

ApplicationContext 的實作類非常之多,還是以下面三種比較典型的場景為例,看看不同環境下的 onRefresh() 做了些什麼事。

Spring容器重新整理—09—onRefresh
  • XmlWebApplicationContext: 傳統的 spring 和 Tomcat 等容器整合的方式,預設使用的 ApplicationContext 實作類
  • ReactiveWebServerApplicationContext: spring-boot 環境下,異步 的 Web 項目預設使用的 ApplicationContext 實作類
  • ServletWebServerApplicationContext: spring-boot 環境下,同步 的 Web 項目預設使用的 ApplicationContext 實作類

XmlWebApplicationContext

這裡指的是傳統的那種手動整合 spring 和 Tomcat 的方式,類似于下面這樣的配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <context-param>
        <!-- 主要關注一下這個配置項, 如果不配置就從 spring-web.jar 的 `org.springframework.web.context.ContextLoader.ContextLoader.properties` 檔案中擷取 -->
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 省略其他配置 -->
    <!-- 省略其他配置 -->
    <!-- 省略其他配置 -->
</web-app>           

這種場景下,預設的 ApplicationContext 實作類是 XmlWebApplicationContext。

onRefresh 就初始化了一些和 WebUI 相關的一些元件,沒有其他特殊的地方。

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
    // ...
}

public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
        implements ConfigurableWebApplicationContext, ThemeSource {

    @Override
    protected void onRefresh() {
        // 初始化了一些和 WebUI 相關的一些元件
        this.themeSource = UiApplicationContextUtils.initThemeSource(this);
    }

}           

WebServer

在繼續看 Web 環境下的 onRefresh() 之前,先看看一個叫做 WebServer 的抽象。就是對 Http 服務的一個抽象。接口聲明如下:

public interface WebServer {
    void start() throws WebServerException;

    void stop() throws WebServerException;

    int getPort();

    default void shutDownGracefully(GracefulShutdownCallback callback) {
        callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
    }
}           

再看看内置的實作:

Spring容器重新整理—09—onRefresh

WebServerFactory

還有一個叫 WebServerFactory 的元件,看名字就知道他就是為了建立上面提到的 WebServer 的工廠類。不同環境有不同的實作類。

Spring容器重新整理—09—onRefresh

其實,Web 環境下的 onRefresh() 主要就是在建立這個 WebServer 或者在建立 WebServerFactory。

ReactiveWebServerApplicationContext

spring-boot 環境下,異步 的 Web 項目:這一步主要是建立了 WebServerFactory。

public class ReactiveWebServerApplicationContext extends GenericReactiveWebApplicationContext
        implements ConfigurableWebServerApplicationContext {

    private volatile WebServerManager serverManager;

    private String serverNamespace;

    @Override
    protected void onRefresh() {
        // AbstractApplicationContext中的空方法
        super.onRefresh();
        try {
            // 建立 WebServer (Netty服務端)
            createWebServer();
        } catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start reactive web server", ex);
        }
    }

    private void createWebServer() {
        WebServerManager serverManager = this.serverManager;
        if (serverManager == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            // 從容器中擷取 ReactiveWebServerFactory 類型的 BeanName: 就是用來建立 WebServer 的工廠類
            String webServerFactoryBeanName = getWebServerFactoryBeanName();

            // 建立/擷取 ReactiveWebServerFactory 單例,beanFactory.getBean(beanName)
            ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
            createWebServer.tag("factory", webServerFactory.getClass().toString());

            // 初始化 WebServer
            boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
            this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);

            // 注冊幾個回收資源的生命周期函數
            getBeanFactory().registerSingleton("webServerGracefulShutdown",
                    new WebServerGracefulShutdownLifecycle(this.serverManager.getWebServer()));
            getBeanFactory().registerSingleton("webServerStartStop",
                    new WebServerStartStopLifecycle(this.serverManager));
            createWebServer.end();
        }
        initPropertySources();
    }
}           

ServletWebServerApplicationContext

spring-boot 環境下,同步 的 Web 項目:這一步主要是建立了 WebServer。

public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {

    @Override
    protected void onRefresh() {
        // GenericWebApplicationContext.onRefresh
        // this.themeSource = UiApplicationContextUtils.initThemeSource(this);
        super.onRefresh();
        try {
            // 建立 WebServer 執行個體
            // 過程和上面提到的 ReactiveWebServerApplicationContext.createWebServer() 大同小異
            createWebServer();
        } catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            ServletWebServerFactory factory = getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            this.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();
    }
}

public class GenericWebApplicationContext extends GenericApplicationContext
        implements ConfigurableWebApplicationContext, ThemeSource {

    @Nullable
    private ThemeSource themeSource;

    @Override
    protected void onRefresh() {
        this.themeSource = UiApplicationContextUtils.initThemeSource(this);
    }
}