溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring容器刷新obtainFreshBeanFactory的方法是什么

發(fā)布時間:2023-03-20 17:01:01 來源:億速云 閱讀:107 作者:iii 欄目:開發(fā)技術

本篇內容主要講解“Spring容器刷新obtainFreshBeanFactory的方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Spring容器刷新obtainFreshBeanFactory的方法是什么”吧!

BeanFactory和ApplicationContext

你要是看 spring 源碼會發(fā)現(xiàn) BeanFactory 的實現(xiàn)類相當多,而且還有各種子接口以及子接口的實現(xiàn)類。

ApplicationContextBeanFactory,但是你不能說 BeanFactoryApplicationContext。

ApplicationContext 實現(xiàn)了 BeanFactory 的同時增強了 BeanFactory,所謂的增強大體上指的是上圖中 ApplicationContext 實現(xiàn)的除了 BeanFactory 接口之外的其他接口的功能。

本文僅僅列出常用的兩類實現(xiàn)。如下圖所示:

Spring容器刷新obtainFreshBeanFactory的方法是什么

那么這么多的實現(xiàn)類,實際應用中到底實例化的是哪個?

這個問題嘛…… 看情況……

上圖列出來兩大類實現(xiàn):

  • xml 版的實現(xiàn): 大體上指的是(不是絕對) AbstractRefreshableApplicationContext 的子類

    • 也就是當年我們哼哧哼哧集成 springservlet 那個年代的事情

    • 大多數(shù)不就是 ClassPathXmlApplicationContext 嗎?(略過)

  • 注解版 的實現(xiàn): 大體上指的是(不是絕對) GenericApplicationContext 的子類

    • Webflux 環(huán)境下一般是 AnnotationConfigReactiveWebServerApplicationContext

    • Servlet 環(huán)境下一般是 AnnotationConfigServletWebServerApplicationContext

這里提到的實現(xiàn)類,無論是哪個,都是派生自 AbstractApplicationContext 的。他們都是 BeanFactory。

既然有 N 個 BeanFactory 的實現(xiàn)類,那么我們應用程序中到底使用的是哪一個呢? 文章末尾再說,先把我們本期的重點 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;
}

下面看看 AbstractRefreshableApplicationContextGenericApplicationContext 這兩種經典的實現(xiàn)類中 refreshBeanFactory()getBeanFactory() 方法的的邏輯。

1.GenericApplicationContext系列的實現(xiàn)

GenericApplicationContextobtainFreshBeanFactory() 的實現(xiàn)幾乎什么也沒做:

  • 所有對 Bean的注冊 相關的方法都委托給了內部維護的 DefaultListableBeanFactory beanFactory。

  • 該系列的實現(xiàn)是不支持多次刷新操作的

  • refreshBeanFactory() 也僅僅是給內部的 beanFactory 初始化了一個 ID。

  • getBeanFactory() 的實現(xiàn)更干脆: 直接將內部維護的 beanFactory 返回接結束了

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    // 和 BeanDefinitionRegistry 相關的方法都委托給了 `beanFactory` 這個成員變量
    private final DefaultListableBeanFactory beanFactory;
    // 狀態(tài)位: 當前容器是不是已經 `刷新`過了, 也就是說 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 這種只支持刷新一次)。

內部也維護著一個 DefaultListableBeanFactory beanFactory, 值得注意的是這個 beanFactory 是被 volatile 修飾的(涉及到多次刷新,頻繁修改 beanFactory 的引用指向)。

refreshBeanFactory() 的實現(xiàn)分為兩大步驟:

  • 銷毀之前可能存在的舊的 beanFactory

    destroyBeans: 銷毀 beanFactory 中所有單例(畢竟此時beanFactory都要銷毀了,beanFactory中的單例肯定要順帶給銷毀掉)

    closeBeanFactory: 實際上就是 this.beanFactory = null;

  • 新建一個 beanFactory 并做一些必要的初始化

    DefaultListableBeanFactory temp = createBeanFactory();: 重新創(chuàng)建一個 BeanFactory 實例

    temp.setSerializationId(getId());: 設置 id

    customizeBeanFactory(temp); 實際上就是給 allowCircularReferencesallowBeanDefinitionOverriding 賦值

    loadBeanDefinitions(temp); 給新創(chuàng)建的 BeanFactory 中加載 BeanDefinition

    this.beanFactory = temp; 新創(chuàng)建的 temp 上位

    • 這一步是抽象方法,不同子類的實現(xiàn)不同

    • 但基本上都是委托各種各樣的 BeanDefinitionReader 給新創(chuàng)建的 BeanFactory 中添加 BeanDefinition。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
    // 是否允許覆蓋重復的 Bean 定義信息
    @Nullable
    private Boolean allowBeanDefinitionOverriding;
    // 當前容器是不是要支持循環(huán)依賴(spring-boot-2.6中默認禁用)
    @Nullable
    private Boolean allowCircularReferences;
    // 刷新之前可能已經存在的一個 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: 刷新之前已經有一個 beanFactory 
            // 銷毀舊的 beanFactory
            // 1. 調用的實際是: 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;
    }
}

AbstractRefreshableApplicationContextgetBeanFactory() 的實現(xiàn)也僅僅是返回了 this.beanFactory。

該使用哪個BeanFactory?

ApplicationContext 的實現(xiàn)類有一大堆,在應用程序中到底怎么確定使用哪個實現(xiàn)類的呢?下面就以傳統(tǒng)的 Servlet 環(huán)境和 spring-boot 環(huán)境為例大概看一下流程。

Servlet環(huán)境

在傳統(tǒng)的 Servlet 環(huán)境下,都會配置一個 ContextLoaderListener 來加載上下文。

  • 獲取名為 contextClassServlet 初始化參數(shù)

  • 如果能獲取到 contextClass 配置, 就直接反射創(chuàng)建一個 contextClass 指定的類作為 ApplicationContext

  • 如果獲取不到 contextClass 配置,就走默認策略

    • 所謂默認策略就是從 spring-web.jarorg.springframework.web.context.ContextLoader.ContextLoader.properties 文件中讀取默認的 WebApplicationContext 實現(xiàn)類型

    • 默認的 WebApplicationContext 的實現(xiàn)類是 XmlWebApplicationContext

下面是和這個過程相關的幾個源碼文件:

  • 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>
        <!-- 主要關注一下這個配置項, 如果不配置就從 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&lt;?&gt; 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&lt;?&gt; 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-bootweb 項目。他是通過 ApplicationContextFactory 來創(chuàng)建 ApplicationContext。

ApplicationContextFactory 就是一個專門用來生產 ApplicationContext 的工廠類。源碼如下,具體細節(jié)會在 spring-boot 相關系列文章中提到,此處先略過。

@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的方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI