Spring容器刷新obtainFreshBeanFactory示例詳解
Spring容器刷新—02—obtainFreshBeanFactory
先聲明一下,這篇文章是原創(chuàng),不過首發(fā)在今日頭條。

這次的內(nèi)容是上圖中的第2步,大概內(nèi)容就是創(chuàng)建一個 BeanFactory 的實例。
BeanFactory和ApplicationContext
你要是看 spring 源碼會發(fā)現(xiàn) BeanFactory 的實現(xiàn)類相當多,而且還有各種子接口以及子接口的實現(xiàn)類。
ApplicationContext 是 BeanFactory,但是你不能說 BeanFactory 是 ApplicationContext。
ApplicationContext 實現(xiàn)了 BeanFactory 的同時增強了 BeanFactory,所謂的增強大體上指的是上圖中 ApplicationContext 實現(xiàn)的除了 BeanFactory 接口之外的其他接口的功能。
本文僅僅列出常用的兩類實現(xiàn)。如下圖所示:

那么這么多的實現(xiàn)類,實際應(yīng)用中到底實例化的是哪個?
這個問題嘛…… 看情況……
上圖列出來兩大類實現(xiàn):
xml版的實現(xiàn): 大體上指的是(不是絕對)AbstractRefreshableApplicationContext的子類- 也就是當年我們哼哧哼哧集成
spring和servlet那個年代的事情 - 大多數(shù)不就是
ClassPathXmlApplicationContext嗎?(略過)
- 也就是當年我們哼哧哼哧集成
- 注解版 的實現(xiàn): 大體上指的是(不是絕對)
GenericApplicationContext的子類- Webflux 環(huán)境下一般是
AnnotationConfigReactiveWebServerApplicationContext - Servlet 環(huán)境下一般是
AnnotationConfigServletWebServerApplicationContext
- Webflux 環(huán)境下一般是
這里提到的實現(xiàn)類,無論是哪個,都是派生自 AbstractApplicationContext 的。他們都是 BeanFactory。
既然有 N 個 BeanFactory 的實現(xiàn)類,那么我們應(yīng)用程序中到底使用的是哪一個呢? 文章末尾再說,先把我們本期的重點 obtainFreshBeanFactory() 的邏輯介紹完。
obtainFreshBeanFactory
obtainFreshBeanFactory() 的工作就是在刷新之前搞到一個 熱乎的 BeanFactory 實例,涉及到的兩個方法都是由子類實現(xiàn)的。
refreshBeanFactory(): 刷新BeanFactory,由不同的子類提供各自的實現(xiàn)。getBeanFactory(): 返回當前ApplicationContext中創(chuàng)建好的BeanFactory,簡單的實現(xiàn)往往就是一個getter方法。
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
/**
* Subclasses must implement this method to perform the actual configuration load.
* The method is invoked by {@link #refresh()} before any other initialization work.
* <p>A subclass will either create a new bean factory and hold a reference to it,
* or return a single BeanFactory instance that it holds. In the latter case, it will
* usually throw an IllegalStateException if refreshing the context more than once.
* @throws BeansException if initialization of the bean factory failed
* @throws IllegalStateException if already initialized and multiple refresh
* attempts are not supported
*/
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
/**
* Subclasses must return their internal bean factory here. They should implement the
* lookup efficiently, so that it can be called repeatedly without a performance penalty.
* <p>Note: Subclasses should check whether the context is still active before
* returning the internal bean factory. The internal factory should generally be
* considered unavailable once the context has been closed.
* @return this application context's internal bean factory (never {@code null})
* @throws IllegalStateException if the context does not hold an internal bean factory yet
* (usually if {@link #refresh()} has never been called) or if the context has been
* closed already
* @see #refreshBeanFactory()
* @see #closeBeanFactory()
*/
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
下面看看 AbstractRefreshableApplicationContext 和 GenericApplicationContext 這兩種經(jīng)典的實現(xiàn)類中 refreshBeanFactory() 和 getBeanFactory() 方法的的邏輯。
1.GenericApplicationContext系列的實現(xiàn)
GenericApplicationContext 對 obtainFreshBeanFactory() 的實現(xiàn)幾乎什么也沒做:
- 所有對 Bean的注冊 相關(guān)的方法都委托給了內(nèi)部維護的
DefaultListableBeanFactory beanFactory。 - 該系列的實現(xiàn)是不支持多次刷新操作的
refreshBeanFactory()也僅僅是給內(nèi)部的beanFactory初始化了一個ID。getBeanFactory()的實現(xiàn)更干脆: 直接將內(nèi)部維護的beanFactory返回接結(jié)束了
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
// 和 BeanDefinitionRegistry 相關(guān)的方法都委托給了 `beanFactory` 這個成員變量
private final DefaultListableBeanFactory beanFactory;
// 狀態(tài)位: 當前容器是不是已經(jīng) `刷新`過了, 也就是說 GenericApplicationContext 是不支持多次刷新操作的
private final AtomicBoolean refreshed = new AtomicBoolean();
/**
* Do nothing: We hold a single internal BeanFactory and rely on callers
* to register beans through our public methods (or the BeanFactory's).
* @see #registerBeanDefinition
*/
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
}
2.AbstractRefreshableApplicationContext系列的實現(xiàn)
看 AbstractRefreshableApplicationContext 的名字就能知道,這個系列的實現(xiàn)是支持多次刷新操作的(不像上面說的 GenericApplicationContext 這種只支持刷新一次)。
內(nèi)部也維護著一個 DefaultListableBeanFactory beanFactory, 值得注意的是這個 beanFactory 是被 volatile 修飾的(涉及到多次刷新,頻繁修改 beanFactory 的引用指向)。
對 refreshBeanFactory() 的實現(xiàn)分為兩大步驟:
- 銷毀之前可能存在的舊的
beanFactorydestroyBeans: 銷毀beanFactory中所有單例(畢竟此時beanFactory都要銷毀了,beanFactory中的單例肯定要順帶給銷毀掉)closeBeanFactory: 實際上就是this.beanFactory = null; - 新建一個
beanFactory并做一些必要的初始化DefaultListableBeanFactory temp = createBeanFactory();: 重新創(chuàng)建一個BeanFactory實例temp.setSerializationId(getId());: 設(shè)置idcustomizeBeanFactory(temp);實際上就是給allowCircularReferences和allowBeanDefinitionOverriding賦值loadBeanDefinitions(temp);給新創(chuàng)建的BeanFactory中加載BeanDefinition- 這一步是抽象方法,不同子類的實現(xiàn)不同
- 但基本上都是委托各種各樣的
BeanDefinitionReader給新創(chuàng)建的BeanFactory中添加BeanDefinition。
this.beanFactory = temp;新創(chuàng)建的temp上位
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
// 是否允許覆蓋重復的 Bean 定義信息
@Nullable
private Boolean allowBeanDefinitionOverriding;
// 當前容器是不是要支持循環(huán)依賴(spring-boot-2.6中默認禁用)
@Nullable
private Boolean allowCircularReferences;
// 刷新之前可能已經(jīng)存在的一個 beanFactory
// 每次刷新都會將當前 beanFactory 銷毀重建
@Nullable
private volatile DefaultListableBeanFactory beanFactory;
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { // this.beanFactory != null: 刷新之前已經(jīng)有一個 beanFactory
// 銷毀舊的 beanFactory
// 1. 調(diào)用的實際是: getBeanFactory().destroySingletons();
destroyBeans();
// 2. this.beanFactory = null;
closeBeanFactory();
}
try {
// 1. 重新創(chuàng)建一個 beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 2.
beanFactory.setSerializationId(getId());
// 3. 實際上是給 allowBeanDefinitionOverriding 和 allowCircularReferences 賦值
customizeBeanFactory(beanFactory);
// 4. 這是一個抽象方法: 就是給新創(chuàng)建的 beanFactory 中加載 `BeanDefinition`
// BeanDefinition 的加載一般都是在子類中委托給了各種 `BeanDefinitionReader`
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
@Override
protected final void closeBeanFactory() {
DefaultListableBeanFactory beanFactory = this.beanFactory;
if (beanFactory != null) {
beanFactory.setSerializationId(null);
this.beanFactory = null;
}
}
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
DefaultListableBeanFactory beanFactory = this.beanFactory;
if (beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return beanFactory;
}
}
AbstractRefreshableApplicationContext 對 getBeanFactory() 的實現(xiàn)也僅僅是返回了 this.beanFactory。
該使用哪個BeanFactory?
ApplicationContext 的實現(xiàn)類有一大堆,在應(yīng)用程序中到底怎么確定使用哪個實現(xiàn)類的呢?下面就以傳統(tǒng)的 Servlet 環(huán)境和 spring-boot 環(huán)境為例大概看一下流程。
Servlet環(huán)境
在傳統(tǒng)的 Servlet 環(huán)境下,都會配置一個 ContextLoaderListener 來加載上下文。
- 獲取名為
contextClass的Servlet初始化參數(shù) - 如果能獲取到
contextClass配置, 就直接反射創(chuàng)建一個contextClass指定的類作為ApplicationContext - 如果獲取不到
contextClass配置,就走默認策略- 所謂默認策略就是從 spring-web.jar 的
org.springframework.web.context.ContextLoader.ContextLoader.properties文件中讀取默認的WebApplicationContext實現(xiàn)類型 - 默認的
WebApplicationContext的實現(xiàn)類是XmlWebApplicationContext
- 所謂默認策略就是從 spring-web.jar 的
下面是和這個過程相關(guān)的幾個源碼文件:
web.xml
<?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>
<!-- 主要關(guān)注一下這個配置項, 如果不配置就從 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>
ContextLoaderListener.java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
// 就是在這里初始化 ApplicationContext 的
@Override
public void contextInitialized(ServletContextEvent event) {
// 父類 ContextLoader 中的方法
initWebApplicationContext(event.getServletContext());
}
}
ContextLoader.java
public class ContextLoader {
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
// 去 classpath 下加載 `ContextLoader.properties`
// 這個文件在 spring-web.jar 的 `org.springframework.web.context.ContextLoader.ContextLoader.properties`
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
// 這里就是創(chuàng)建具體的 ApplicationContext 實例
// 因為是 web 環(huán)境,所以創(chuàng)建的是 `WebApplicationContext` 的實現(xiàn)類的實例
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 這里才是確定到底創(chuàng)建什么類型的 `WebApplicationContext`
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
protected Class<?> determineContextClass(ServletContext servletContext) {
// CONTEXT_CLASS_PARAM常量值就是: contextClass(在 web.xml 中配置的那個)
// 1. 如果你指定了 `contextClass` 就使用你指定的 `WebApplicationContext` 實現(xiàn)類
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
// 2. 如果沒有指定 `contextClass` 配置就使用 `defaultStrategies` 來
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
} catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
}
ContextLoader.properties
# Default WebApplicationContext implementation class for ContextLoader. # Used as fallback when no explicit context implementation has been specified as context-param. # Not meant to be customized by application developers. # 指定默認的 `WebApplicationContext` 的實現(xiàn)類是: `XmlWebApplicationContext` org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
下面再簡單提一下 spring-boot 環(huán)境中 ApplicationContext 的創(chuàng)建。
SpringBoot環(huán)境
這里特指基于 spring-boot 的 web 項目。他是通過 ApplicationContextFactory 來創(chuàng)建 ApplicationContext。
ApplicationContextFactory 就是一個專門用來生產(chǎn) ApplicationContext 的工廠類。源碼如下,具體細節(jié)會在 spring-boot 相關(guān)系列文章中提到,此處先略過。
@FunctionalInterface
public interface ApplicationContextFactory {
// 省略幾個 default 方法
/**
* Creates the {@link ConfigurableApplicationContext application context} for a
* {@link SpringApplication}, respecting the given {@code webApplicationType}.
* @param webApplicationType the web application type
* @return the newly created application context
*/
ConfigurableApplicationContext create(WebApplicationType webApplicationType);
}
以上就是Spring容器刷新obtainFreshBeanFactory示例詳解的詳細內(nèi)容,更多關(guān)于Spring obtainFreshBeanFactory的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Servlet注解之@WebInitParam多個InitParam的使用
這篇文章主要介紹了Servlet注解之@WebInitParam多個InitParam的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
寧可用Lombok也不把成員設(shè)置為public原理解析
這篇文章主要為大家介紹了寧可用Lombok也不把成員設(shè)置為public原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03

