這次的内容是上圖中的第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() 做了些什麼事。
- 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);
}
}
再看看内置的實作:
WebServerFactory
還有一個叫 WebServerFactory 的元件,看名字就知道他就是為了建立上面提到的 WebServer 的工廠類。不同環境有不同的實作類。
其實,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);
}
}