Spring容器刷新obtainFreshBeanFactory示例詳解
Spring容器刷新—02—obtainFreshBeanFactory
先聲明一下,這篇文章是原創(chuàng),不過(guò)首發(fā)在今日頭條。
這次的內(nèi)容是上圖中的第2步,大概內(nèi)容就是創(chuàng)建一個(gè) BeanFactory
的實(shí)例。
BeanFactory和ApplicationContext
你要是看 spring 源碼會(huì)發(fā)現(xiàn) BeanFactory
的實(shí)現(xiàn)類相當(dāng)多,而且還有各種子接口以及子接口的實(shí)現(xiàn)類。
ApplicationContext
是 BeanFactory
,但是你不能說(shuō) BeanFactory
是 ApplicationContext
。
ApplicationContext
實(shí)現(xiàn)了 BeanFactory
的同時(shí)增強(qiáng)了 BeanFactory
,所謂的增強(qiáng)大體上指的是上圖中 ApplicationContext
實(shí)現(xiàn)的除了 BeanFactory
接口之外的其他接口的功能。
本文僅僅列出常用的兩類實(shí)現(xiàn)。如下圖所示:
那么這么多的實(shí)現(xiàn)類,實(shí)際應(yīng)用中到底實(shí)例化的是哪個(gè)?
這個(gè)問(wèn)題嘛…… 看情況……
上圖列出來(lái)兩大類實(shí)現(xiàn):
xml
版的實(shí)現(xiàn): 大體上指的是(不是絕對(duì))AbstractRefreshableApplicationContext
的子類- 也就是當(dāng)年我們哼哧哼哧集成
spring
和servlet
那個(gè)年代的事情 - 大多數(shù)不就是
ClassPathXmlApplicationContext
嗎?(略過(guò))
- 也就是當(dāng)年我們哼哧哼哧集成
- 注解版 的實(shí)現(xiàn): 大體上指的是(不是絕對(duì))
GenericApplicationContext
的子類- Webflux 環(huán)境下一般是
AnnotationConfigReactiveWebServerApplicationContext
- Servlet 環(huán)境下一般是
AnnotationConfigServletWebServerApplicationContext
- Webflux 環(huán)境下一般是
這里提到的實(shí)現(xiàn)類,無(wú)論是哪個(gè),都是派生自 AbstractApplicationContext
的。他們都是 BeanFactory
。
既然有 N 個(gè) BeanFactory
的實(shí)現(xiàn)類,那么我們應(yīng)用程序中到底使用的是哪一個(gè)呢? 文章末尾再說(shuō),先把我們本期的重點(diǎn) obtainFreshBeanFactory()
的邏輯介紹完。
obtainFreshBeanFactory
obtainFreshBeanFactory()
的工作就是在刷新之前搞到一個(gè) 熱乎的 BeanFactory
實(shí)例,涉及到的兩個(gè)方法都是由子類實(shí)現(xiàn)的。
refreshBeanFactory()
: 刷新BeanFactory
,由不同的子類提供各自的實(shí)現(xiàn)。getBeanFactory()
: 返回當(dāng)前ApplicationContext
中創(chuàng)建好的BeanFactory
,簡(jiǎn)單的實(shí)現(xiàn)往往就是一個(gè)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)典的實(shí)現(xiàn)類中 refreshBeanFactory()
和 getBeanFactory()
方法的的邏輯。
1.GenericApplicationContext系列的實(shí)現(xiàn)
GenericApplicationContext
對(duì) obtainFreshBeanFactory()
的實(shí)現(xiàn)幾乎什么也沒(méi)做:
- 所有對(duì) Bean的注冊(cè) 相關(guān)的方法都委托給了內(nèi)部維護(hù)的
DefaultListableBeanFactory beanFactory
。 - 該系列的實(shí)現(xiàn)是不支持多次刷新操作的
refreshBeanFactory()
也僅僅是給內(nèi)部的beanFactory
初始化了一個(gè)ID
。getBeanFactory()
的實(shí)現(xiàn)更干脆: 直接將內(nèi)部維護(hù)的beanFactory
返回接結(jié)束了
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { // 和 BeanDefinitionRegistry 相關(guān)的方法都委托給了 `beanFactory` 這個(gè)成員變量 private final DefaultListableBeanFactory beanFactory; // 狀態(tài)位: 當(dāng)前容器是不是已經(jīng) `刷新`過(guò)了, 也就是說(shuō) 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系列的實(shí)現(xiàn)
看 AbstractRefreshableApplicationContext
的名字就能知道,這個(gè)系列的實(shí)現(xiàn)是支持多次刷新操作的(不像上面說(shuō)的 GenericApplicationContext
這種只支持刷新一次)。
內(nèi)部也維護(hù)著一個(gè) DefaultListableBeanFactory beanFactory
, 值得注意的是這個(gè) beanFactory
是被 volatile
修飾的(涉及到多次刷新,頻繁修改 beanFactory
的引用指向)。
對(duì) refreshBeanFactory()
的實(shí)現(xiàn)分為兩大步驟:
- 銷毀之前可能存在的舊的
beanFactory
destroyBeans
: 銷毀beanFactory
中所有單例(畢竟此時(shí)beanFactory
都要銷毀了,beanFactory
中的單例肯定要順帶給銷毀掉)closeBeanFactory
: 實(shí)際上就是this.beanFactory = null;
- 新建一個(gè)
beanFactory
并做一些必要的初始化DefaultListableBeanFactory temp = createBeanFactory();
: 重新創(chuàng)建一個(gè)BeanFactory
實(shí)例temp.setSerializationId(getId());
: 設(shè)置id
customizeBeanFactory(temp);
實(shí)際上就是給allowCircularReferences
和allowBeanDefinitionOverriding
賦值loadBeanDefinitions(temp);
給新創(chuàng)建的BeanFactory
中加載BeanDefinition
- 這一步是抽象方法,不同子類的實(shí)現(xiàn)不同
- 但基本上都是委托各種各樣的
BeanDefinitionReader
給新創(chuàng)建的BeanFactory
中添加BeanDefinition
。
this.beanFactory = temp;
新創(chuàng)建的temp
上位
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { // 是否允許覆蓋重復(fù)的 Bean 定義信息 @Nullable private Boolean allowBeanDefinitionOverriding; // 當(dāng)前容器是不是要支持循環(huán)依賴(spring-boot-2.6中默認(rèn)禁用) @Nullable private Boolean allowCircularReferences; // 刷新之前可能已經(jīng)存在的一個(gè) beanFactory // 每次刷新都會(huì)將當(dāng)前 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)有一個(gè) beanFactory // 銷毀舊的 beanFactory // 1. 調(diào)用的實(shí)際是: getBeanFactory().destroySingletons(); destroyBeans(); // 2. this.beanFactory = null; closeBeanFactory(); } try { // 1. 重新創(chuàng)建一個(gè) beanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); // 2. beanFactory.setSerializationId(getId()); // 3. 實(shí)際上是給 allowBeanDefinitionOverriding 和 allowCircularReferences 賦值 customizeBeanFactory(beanFactory); // 4. 這是一個(gè)抽象方法: 就是給新創(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
對(duì) getBeanFactory()
的實(shí)現(xiàn)也僅僅是返回了 this.beanFactory
。
該使用哪個(gè)BeanFactory?
ApplicationContext
的實(shí)現(xiàn)類有一大堆,在應(yīng)用程序中到底怎么確定使用哪個(gè)實(shí)現(xiàn)類的呢?下面就以傳統(tǒng)的 Servlet
環(huán)境和 spring-boot 環(huán)境為例大概看一下流程。
Servlet環(huán)境
在傳統(tǒng)的 Servlet
環(huán)境下,都會(huì)配置一個(gè) ContextLoaderListener
來(lái)加載上下文。
- 獲取名為
contextClass
的Servlet
初始化參數(shù) - 如果能獲取到
contextClass
配置, 就直接反射創(chuàng)建一個(gè)contextClass
指定的類作為ApplicationContext
- 如果獲取不到
contextClass
配置,就走默認(rèn)策略- 所謂默認(rèn)策略就是從 spring-web.jar 的
org.springframework.web.context.ContextLoader.ContextLoader.properties
文件中讀取默認(rèn)的WebApplicationContext
實(shí)現(xiàn)類型 - 默認(rèn)的
WebApplicationContext
的實(shí)現(xiàn)類是XmlWebApplicationContext
- 所謂默認(rèn)策略就是從 spring-web.jar 的
下面是和這個(gè)過(guò)程相關(guān)的幾個(gè)源碼文件:
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)注一下這個(gè)配置項(xiàng), 如果不配置就從 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` // 這個(gè)文件在 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 實(shí)例 // 因?yàn)槭?web 環(huán)境,所以創(chuàng)建的是 `WebApplicationContext` 的實(shí)現(xiàn)類的實(shí)例 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 中配置的那個(gè)) // 1. 如果你指定了 `contextClass` 就使用你指定的 `WebApplicationContext` 實(shí)現(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. 如果沒(méi)有指定 `contextClass` 配置就使用 `defaultStrategies` 來(lái) 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. # 指定默認(rèn)的 `WebApplicationContext` 的實(shí)現(xiàn)類是: `XmlWebApplicationContext` org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
下面再簡(jiǎn)單提一下 spring-boot 環(huán)境中 ApplicationContext
的創(chuàng)建。
SpringBoot環(huán)境
這里特指基于 spring-boot 的 web 項(xiàng)目。他是通過(guò) ApplicationContextFactory
來(lái)創(chuàng)建 ApplicationContext
。
ApplicationContextFactory
就是一個(gè)專門(mén)用來(lái)生產(chǎn) ApplicationContext
的工廠類。源碼如下,具體細(xì)節(jié)會(huì)在 spring-boot 相關(guān)系列文章中提到,此處先略過(guò)。
@FunctionalInterface public interface ApplicationContextFactory { // 省略幾個(gè) 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示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring obtainFreshBeanFactory的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JAVA參數(shù)傳遞方式實(shí)例淺析【按值傳遞與引用傳遞區(qū)別】
這篇文章主要介紹了JAVA參數(shù)傳遞方式,結(jié)合實(shí)例形式分析了java按值傳遞與引用傳遞區(qū)別及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-05-05idea在工具欄中顯示快速創(chuàng)建包和類的圖標(biāo)的詳細(xì)步驟
點(diǎn)擊需要?jiǎng)?chuàng)建包或者類的位置,在點(diǎn)擊對(duì)用的圖標(biāo)就可以快速創(chuàng)建類或者包了,下面小編給大家介紹idea在工具欄中顯示快速創(chuàng)建包和類的圖標(biāo)的詳細(xì)步驟,感興趣的朋友一起看看吧2024-02-02Mybatis分頁(yè)查詢的實(shí)現(xiàn)(Rowbounds和PageHelper)
本文主要介紹了Mybatis分頁(yè)查詢的實(shí)現(xiàn)(Rowbounds和PageHelper),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01Jmeter基于JDBC請(qǐng)求實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)測(cè)試
這篇文章主要介紹了Jmeter基于JDBC請(qǐng)求實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)測(cè)試,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10java基于swing實(shí)現(xiàn)的五子棋游戲代碼
這篇文章主要介紹了java基于swing實(shí)現(xiàn)的五子棋游戲代碼,主要涉及圖形界面與數(shù)組的用法,有不錯(cuò)的參考借鑒價(jià)值,需要的朋友可以參考下2014-11-11java爬蟲(chóng)之使用HttpClient模擬瀏覽器發(fā)送請(qǐng)求方法詳解
這篇文章主要介紹了java爬蟲(chóng)之使用HttpClient模擬瀏覽器發(fā)送請(qǐng)求方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07