主頁 > 後端開發 > 死磕Spring之IoC篇 - 開啟 Bean 的加載

死磕Spring之IoC篇 - 開啟 Bean 的加載

2021-03-02 06:13:36 後端開發

該系列文章是本人在學習 Spring 的程序中總結下來的,里面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼注釋 Spring 原始碼分析 GitHub 地址 進行閱讀

Spring 版本:5.1.14.RELEASE

開始閱讀這一系列文章之前,建議先查看《深入了解 Spring IoC(面試題)》這一篇文章

該系列其他文章請查看:《死磕 Spring 之 IoC 篇 - 文章導讀》

開啟 Bean 的加載

前面的一些列文章對面向資源(XML、Properties)面向注解定義的 Bean 是如何被決議成 BeanDefinition(Bean 的“前身”),并保存至 BeanDefinitionRegistry 注冊中心里面,實際也是通過 ConcurrentHashMap 進行保存,

Spring 底層 IoC 容器 DefaultListableBeanFactory,實作了 BeanFactory 和 BeanDefinitionRegistry 介面,這個時候它處于“就緒狀態”,當我們顯示或者隱式地呼叫 getBean(...) 方法時,會觸發加載 Bean 階段,獲取對應的 Bean,在該方法中,如果是單例模式會先從快取中獲取,已有則直接回傳,沒有則根據 BeanDefinition 開始初始化這個 Bean,

BeanFactory 體系結構

先來看看 BeanFactory 介面的繼承關系

簡單描述這些介面:

  • org.springframework.beans.factory.BeanFactory,Spring IoC 容器最基礎的介面,提供依賴查找單個 Bean 的功能

  • org.springframework.beans.factory.ListableBeanFactory,繼承 BeanFactory 介面,提供依賴查找多個 Bean 的功能

  • org.springframework.beans.factory.HierarchicalBeanFactory,繼承 BeanFactory 介面,提供獲取父 BeanFactory 的功能,具有層次性

  • org.springframework.beans.factory.config.ConfigurableBeanFactory,繼承 HierarchicalBeanFactory 介面,提供可操作內部相關組件的功能,具有可配置性

  • org.springframework.beans.factory.config.AutowireCapableBeanFactory,繼承 BeanFactory 介面,提供可注入的功能,支持依賴注入

  • org.springframework.beans.factory.config.ConfigurableListableBeanFactory,繼承上面所有介面,綜合所有特性,還提供可提前初始化所有單例 Bean 的功能

通過這些介面的名稱可以大致了解其用意,接下來我們來看看它們的實作類的繼承關系

簡單描述這些實作類:

  • org.springframework.beans.factory.support.AbstractBeanFactory 抽象類,實作 ConfigurableBeanFactory 介面,基礎實作類,Bean 的創建程序交由子類實作
  • org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 抽象類,繼承 AbstractBeanFactory,實作 AutowireCapableBeanFactory 介面,完成 Bean 的創建
  • org.springframework.beans.factory.support.DefaultListableBeanFactory,Spring 底層 IoC 容器,依賴注入的底層實作

其他的介面和類和 BeanDefinition 注冊中心,別名注冊中心,單例 Bean 注冊中心相關;右下角的 ApplicationContext 與 Spring 應用背景關系有關,它的整個體系這里不做展述,在后面的文章進行分析

AbstractBeanFactory

org.springframework.beans.factory.support.AbstractBeanFactory 抽象類,實作 ConfigurableBeanFactory 介面,BeanFactory 的基礎實作類,提供依賴查找方法,可獲取 Bean 物件,接下來我們來看看依賴查找的實作

getBean 方法

getBean(String name) 方法,根據名稱獲取 Bean,當然還有許多多載方法,如下:

@Override
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    return doGetBean(name, requiredType, null, false);
}

@Override
public Object getBean(String name, Object... args) throws BeansException {
    return doGetBean(name, null, args, false);
}

public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
        throws BeansException {
    return doGetBean(name, requiredType, args, false);
}

最終都會呼叫 doGetBean(...) 這個方法

當我們顯示或者隱式地呼叫這個方法時,會觸發 Bean 的加載;你是否會有疑問,我們使用 Spring 的程序中并不會呼叫這個方法去獲取 Bean,那這個方法會被誰呼叫呢?在 ConfigurableListableBeanFactory 介面中提供提前初始化所有單例 Bean 的功能,在 Spring 應用背景關系(ApplicationContext)重繪階段會提前初始化所有的單例 Bean,這個提前初始化也是呼叫 getBean 這個方法,這部分內容在后續分析 Spring 應用背景關系的生命周期會講到

【核心】doGetBean 方法

doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) 方法,獲取一個 Bean,方法如下:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

    // <1> 獲取 `beanName`
    // 因為入參 `name` 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(`&` 開頭,需要去除)
    // 所以需要獲取真實的 beanName
    final String beanName = transformedBeanName(name);
    Object bean;

    // <2> 先從快取(僅快取單例 Bean )中獲取 Bean 物件,這里快取指的是 `3` 個 Map
    // 快取中也可能是正在初始化的 Bean,可以避免**回圈依賴注入**引起的問題
    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    // <3> 若從快取中獲取到對應的 Bean,且 `args` 引數為空
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        // <3.1> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
        // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // 快取中沒有對應的 Bean,則開啟 Bean 的加載
    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        // <4> 如果**非單例模式**下的 Bean 正在創建,這里又開始創建,表明存在回圈依賴,則直接拋出例外
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        BeanFactory parentBeanFactory = getParentBeanFactory();
        // <5> 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中加載(如果存在父容器)
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            // <5.1> 獲取 `beanName`,因為可能是別名,則進行處理
            // 和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查找
            String nameToLookup = originalBeanName(name);
            // <5.2> 若為 AbstractBeanFactory 型別,委托父容器的 doGetBean 方法進行處理
            // 否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }

        // <6> 如果不是僅僅做型別檢查,則表示需要創建 Bean,將 `beanName` 標記為已創建過
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            // <7> 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合并后)
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 檢查是否為抽象類
            checkMergedBeanDefinition(mbd, beanName, args);

            // Guarantee initialization of beans that the current bean depends on.
            // <8> 獲取當前正在創建的 Bean 所依賴物件集合(`depends-on` 配置的依賴)
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // <8.1> 檢測是否存在回圈依賴,存在則拋出例外
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // <8.2> 將 `beanName` 與 `dep` 之間依賴的關系進行快取
                    registerDependentBean(dep, beanName);
                    try {
                        // <8.3> 先創建好依賴的 Bean(重新呼叫 `getBean(...)` 方法)
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // Create bean instance.
            // <9> 開始創建 Bean,不同模式創建方式不同
            if (mbd.isSingleton()) { // <9.1> 單例模式
                /*
                 * <9.1.1> 創建 Bean,成功創建則進行快取,并移除快取的早期物件
                 * 創建程序實際呼叫的下面這個 `createBean(...)` 方法
                 */
                sharedInstance = getSingleton(beanName,
                        // ObjectFactory 實作類
                        () -> {
                            try {
                                // **【核心】** 創建 Bean
                                return createBean(beanName, mbd, args);
                            } catch (BeansException ex) {
                                // Explicitly remove instance from singleton cache: It might have been put there
                                // eagerly by the creation process, to allow for circular reference resolution.
                                // Also remove any beans that received a temporary reference to the bean.
                                // 如果創建程序出現例外,則顯式地從快取中洗掉當前 Bean 相關資訊
                                // 在單例模式下為了解決回圈依賴,創建程序會快取早期物件,這里需要進行洗掉
                                destroySingleton(beanName);
                                throw ex;
                            }
                });
                // <9.1.2> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
                // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // <9.2> 原型模式
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    // <9.2.1> 將 `beanName` 標記為原型模式正在創建
                    beforePrototypeCreation(beanName);
                    // <9.2.2> **【核心】** 創建 Bean
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    // <9.2.3> 將 `beanName` 標記為不在創建中,照應第 `9.2.1` 步
                    afterPrototypeCreation(beanName);
                }
                // <9.2.4> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
                // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            // <9.3> 其他模式
            else {
                // <9.3.1> 獲取該模式的 Scope 物件 `scope`,不存在則拋出例外
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    // <9.3.1> 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實作),不存在則執行**原型模式**的四個步驟進行創建
                    Object scopedInstance = scope.get(beanName, () -> {
                        // 將 `beanName` 標記為原型模式正在創建
                        beforePrototypeCreation(beanName);
                        try {
                            // **【核心】** 創建 Bean
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            // 將 `beanName` 標記為不在創建中,照應上一步
                            afterPrototypeCreation(beanName);
                        }
                    });
                    // 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
                    // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // Check if required type matches the type of the actual bean instance.
    // <10> 如果入參 `requiredType` 不為空,并且 Bean 不是該型別,則需要進行型別轉換
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            // <10.1> 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            // <10.2> 轉換后的 Bean 為空則拋出例外
            if (convertedBean == null) {
                // 轉換失敗,拋出 BeanNotOfRequiredTypeException 例外
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            // <10.3> 回傳型別轉換后的 Bean 物件
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isTraceEnabled()) {
                logger.trace("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    // <11> 回傳獲取到的 Bean
    return (T) bean;
}

這個方法的處理程序有點長,如下:

  1. 獲取 beanName,因為入參 name 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(& 開頭,需要去除),所以需要獲取真實的 beanName

  2. 先從快取(僅快取單例 Bean )中獲取 Bean 物件,這里快取指的是 3 個 Map;快取中也可能是正在初始化的 Bean,可以避免回圈依賴注入引起的問題

  3. 若從快取中獲取到對應的 Bean,且 args 引數為空

    1. 【同】呼叫 getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) 方法

      獲取 Bean 的目標物件,scopedInstance 非 FactoryBean 型別直接回傳,否則,呼叫 FactoryBean#getObject() 獲取目標物件


快取中沒有對應的 Bean,則開啟 Bean 的加載

  1. 如果非單例模式下的 Bean 正在創建,這里又開始創建,表明存在回圈依賴,則直接拋出例外

  2. 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中加載(如果存在父容器)

    1. 獲取 beanName,因為可能是別名,則進行處理,和第 1 步不同,不需要對 & 進行處理,因為進入父容器重新依賴查找
    2. 若為 AbstractBeanFactory 型別,委托父容器的 doGetBean 方法進行處理;否則,就是非 Spring IoC 容器,根據引數呼叫相應的 getBean(...)方法
  3. 如果不是僅僅做型別檢查,則表示需要創建 Bean,將 beanName 標記為已創建過,在后面的回圈依賴檢查中會使用到

  4. 從容器中獲取 beanName 對應的的 RootBeanDefinition(合并后),呼叫 getMergedLocalBeanDefinition(String beanName) 方法

  5. 獲取當前正在創建的 Bean 所依賴物件集合(depends-on 配置的依賴)

    1. 檢測是否存在回圈依賴,存在則拋出例外
    2. beanNamedep 之間依賴的關系進行快取
    3. 先創建好依賴的 Bean(重新呼叫 getBean(...) 方法)

  1. 開始創建 Bean,不同模式創建方式不同

    1. 單例模式

      1. 創建 Bean,成功創建則進行快取,并移除快取的早期物件,呼叫 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法

        【核心】入參的 ObjectFactory 實作類就是呼叫的 AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) 方法

      2. 【同】 和上面的 3.1 相同操作

    2. 原型模式

      1. beanName 標記為非單例模式正在創建
      2. 【核心】 創建 Bean,呼叫 AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]) 方法
      3. beanName 標記為不在創建中,照應第 9.2.1
      4. 【同】 和上面的 3.1 相同操作
    3. 其他模式

      1. 獲取該模式的 Scope 物件 scope,不存在則拋出例外
      2. scope 中獲取 beanName 對應的物件(看你的具體實作),不存在則執行原型模式的四個步驟進行創建

  1. 如果入參 requiredType 不為空,并且 Bean 不是該型別,則需要進行型別轉換

    1. 通過型別轉換機制,將 Bean 轉換成 requiredType 型別
    2. 轉換后的 Bean 為空則拋出例外
    3. 回傳型別轉換后的 Bean 物件
  2. 回傳獲取到的 Bean


概括:

  • 可以看到這個方法加載 Bean 的程序中,會先從快取中獲取單例模式的 Bean;

  • 不管是從快取中獲取的還是新創建的,都會進行處理,如果是 FactoryBean 型別則呼叫其 getObject() 獲取目標物件;

  • BeanFactory 可能有父容器,如果當前容器找不到 BeanDefinition 則會嘗試讓父容器創建;

  • 創建 Bean 的任務交由 AbstractAutowireCapableBeanFactory 去完成;

  • 如果獲取到的 Bean 不是我們想要型別,會通過型別轉換機制轉換成目標型別

接下來依次分析上述程序的相關步驟(doGetBean(...)

1. 獲取 beanName

對應代碼段:

// AbstractBeanFactory.java
final String beanName = transformedBeanName(name);

因為入參 name 可能是別名,也可能是 FactoryBean 型別 Bean 的名稱(& 開頭,需要去除),所以需要進行一番轉換,如下:

// AbstractBeanFactory.java
protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// BeanFactoryUtils.java
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    // 獲取 name 對應的 beanName,
    // 不為 null 則回傳 `transformedBeanNameCache` 快取中對應的 beanName,
    // 為 null 則對 name 進行處理,將前綴 '&' 去除,直至沒有 '&',然后放入 `transformedBeanNameCache` 快取中,并回傳處理后的 beanName
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}
// SimpleAliasRegistry.java
public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    // 回圈,從 aliasMap 中,獲取到最終的 beanName
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

程序并不復雜,先將前綴 & 去除(如果存在),如果是別名則獲取對應的 beanName

定義了一個 FactoryBean 型別的 Bean,名稱為 user,通過 user 獲取 Bean,獲取到的是 FactoryBean#getObject() 回傳的物件(只會被呼叫一次)

通過 &user 獲取 Bean,獲取到的是 FactoryBean 本身這個物件

2. 從快取中獲取單例 Bean

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
Object sharedInstance = getSingleton(beanName);

單例模式的 Bean 被創建后會快取,為了避免回圈依賴注入,在創建程序會臨時快取正在創建的 Bean(早期 Bean),在后續文章會講到,從快取中獲取物件程序如下:

// DefaultSingletonBeanRegistry.java

public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // <1> **【一級 Map】**從單例快取 `singletonObjects` 中獲取 beanName 對應的 Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    // <2> 如果**一級 Map**中不存在,且當前 beanName 正在創建
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // <2.1> 對 `singletonObjects` 加鎖
        synchronized (this.singletonObjects) {
            // <2.2> **【二級 Map】**從 `earlySingletonObjects` 集合中獲取,里面會保存從 **三級 Map** 獲取到的正在初始化的 Bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            // <2.3> 如果**二級 Map** 中不存在,且允許提前創建
            if (singletonObject == null && allowEarlyReference) {
                // <2.3.1> **【三級 Map】**從 `singletonFactories` 中獲取對應的 ObjectFactory 實作類
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                // 如果從**三級 Map** 中存在對應的物件,則進行下面的處理
                if (singletonFactory != null) {
                    // <2.3.2> 呼叫 ObjectFactory#getOject() 方法,獲取目標 Bean 物件(早期半成品)
                    singletonObject = singletonFactory.getObject();
                    // <2.3.3> 將目標物件放入**二級 Map**
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // <2.3.4> 從**三級 Map**移除 `beanName`
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    // <3> 回傳從快取中獲取的物件
    return singletonObject;
}

程序如下:

  1. 【一級 Map】從單例快取 singletonObjects 中獲取 beanName 對應的 Bean
  2. 如果一級 Map中不存在,且當前 beanName 正在創建
    1. singletonObjects 加鎖
    2. 【二級 Map】earlySingletonObjects 集合中獲取,里面會保存從 三級 Map 獲取到的正在初始化的 Bean
    3. 如果二級 Map 中不存在,且允許提前創建
      1. 【三級 Map】singletonFactories 中獲取對應的 ObjectFactory 實作類,如果從三級 Map 中存在對應的物件,則進行下面的處理
      2. 呼叫 ObjectFactory#getOject() 方法,獲取目標 Bean 物件(早期半成品)
      3. 將目標物件放入二級 Map
      4. 三級 Map移除 beanName
  3. 回傳從快取中獲取的物件

這個程序對應《深入了解 Spring IoC(面試題)》中的BeanFactory 是如何處理回圈依賴問題

3. FactoryBean 的處理

一般情況下,Spring 通過反射機制利用 Bean 的 beanClass 屬性指定實作類來實體化 Bean,某些情況下,Bean 的實體化程序比較復雜,如果按照傳統的方式,則需要提供大量的配置資訊,配置方式的靈活性有限,這時采用編碼的方式可能會得到一個簡單的方案,Spring 為此提供了一個 FactoryBean 的工廠 Bean 介面,用戶可以通過實作該介面定制實體化 Bean 的邏輯,

FactoryBean 介面對于 Spring 框架本身也非常重要,其內部就提供了大量 FactoryBean 的實作,它們隱藏了實體化程序中一些復雜細節,給上層應用帶來了便利,

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

不管是從快取中獲取的還是新創建的,都會呼叫這個方法進行處理,如果是 FactoryBean 型別則呼叫其 getObject() 獲取目標物件

getObjectForBeanInstance 方法

// AbstractBeanFactory.java
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, 
                                          @Nullable RootBeanDefinition mbd) {
    // Don't let calling code try to dereference the factory if the bean isn't a factory.
    // <1> 若 `name` 以 `&` 開頭,說明想要獲取 FactoryBean,則校驗其**正確性**
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // <1.1> 如果是 NullBean 空物件,則直接回傳
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // <1.2> 如果不是 FactoryBean 型別,則拋出例外
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
    }

    // Now we have the bean instance, which may be a normal bean or a FactoryBean.
    // If it's a FactoryBean, we use it to create a bean instance, unless the caller actually wants a reference to the factory.
    // 到這里我們就有了一個 Bean,可能是一個正常的 Bean,也可能是一個 FactoryBean
    // 如果是 FactoryBean,則需要通過其 getObject() 方法獲取目標物件

    // <2> 如果 `beanInstance` 不是 FactoryBean 型別,不需要再處理則直接回傳
    // 或者(表示是 FactoryBean 型別) `name` 以 `&` 開頭,表示你想要獲取實際 FactoryBean 物件,則直接回傳
    // 還不符合條件的話,表示是 FactoryBean,需要獲取 getObject() 回傳目標物件
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    // <3> 如果入參沒有傳 BeanDefinition,則從 `factoryBeanObjectCache` 快取中獲取對應的 Bean 物件
	// 入參傳了 BeanDefinition 表示這個 Bean 是剛創建的,不走快取,需要呼叫其 getObject() 方法獲取目標物件
	// `factoryBeanObjectCache`:FactoryBean#getObject() 呼叫一次后回傳的目標物件快取在這里
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    // <4> 若第 `3` 步獲取的物件為空,則需要呼叫 FactoryBean#getObject() 獲得物件
    if (object == null) {
        // Return bean instance from factory.
        // <4.1> 將 `beanInstance` 轉換成 FactoryBean 型別
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        // <4.2> 如果入參沒有傳 BeanDefinition 并且當前容器存在對應的 BeanDefinition
        if (mbd == null && containsBeanDefinition(beanName)) {
            // 獲取對應的 RootBeanDefinition(合并后)
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // 是否是用戶定義的(不是 Spring 創建決議出來的)
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // <4.3> **【核心】**通過 FactoryBean 獲得目標物件,單例模式會快取在 `factoryBeanObjectCache` 中
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

程序如下:

  1. name& 開頭,說明想要獲取 FactoryBean,則校驗其正確性

    1. 如果是 NullBean 空物件,則直接回傳
    2. 如果不是 FactoryBean 型別,則拋出例外
  2. 如果 beanInstance 不是 FactoryBean 型別,不需要再處理則直接回傳;或者(表示是 FactoryBean 型別) name& 開頭,表示你想要獲取實際 FactoryBean 物件,則直接回傳;還不符合條件的話,表示是 FactoryBean,需要獲取 getObject() 回傳目標物件,往下處理

  3. 如果入參沒有傳 BeanDefinition,則從 factoryBeanObjectCache 快取中獲取對應的 Bean 物件,如下:

    // FactoryBeanRegistrySupport.java
    protected Object getCachedObjectForFactoryBean(String beanName) {
        return this.factoryBeanObjectCache.get(beanName);
    }
    

    factoryBeanObjectCache:FactoryBean#getObject() 呼叫一次后回傳的目標物件快取在這里

    入參傳了 BeanDefinition 表示這個 Bean 是剛創建的,不走快取,需要呼叫其 getObject() 方法獲取目標物件

  4. 若第 3 步獲取的物件為空,則需要呼叫 FactoryBean#getObject() 獲得物件

    1. beanInstance 轉換成 FactoryBean 型別
    2. 如果入參沒有傳 BeanDefinition 并且當前容器存在對應的 BeanDefinition,則獲取對應的 RootBeanDefinition(合并后)
    3. 【核心】通過 FactoryBean 獲得目標物件,單例模式會快取在 factoryBeanObjectCache 中,呼叫 getObjectFromFactoryBean(FactoryBean<?>, String, boolean) 方法

getObjectFromFactoryBean 方法

getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) 方法,獲取 FactoryBean 的目標物件,方法如下:

// FactoryBeanRegistrySupport.java
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // <1> `factory` 為單例模式,且單例 Bean 快取中存在 `beanName` 對應的 FactoryBean 物件
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) { // <1.1> 獲取單例鎖,保證安全
            // <1.2> 從 `factoryBeanObjectCache` 快取中獲取 FactoryBean#getObject() 創建的目標物件
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // <1.3> 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                // <1.4> 這里再進行一次校驗,看是否在快取中存在 FactoryBean 創建的目標物件,如果有則優先從快取中獲取
                // 保證 FactoryBean#getObject() 只能被呼叫一次
                // 沒有的話,則對付訓取到的目標物件進行接下來的處理
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                } else {
                    // <1.5> 是否需要后續處理,這個 FactoryBean 的前身 BeanDefinition 是否由 Spring 決議出來的,通常情況下都是
                    if (shouldPostProcess) {
                        // <1.5.1> 若該 FactoryBean 處于創建中,則直接回傳這個目標物件,不進行接下來的處理程序
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        // <1.5.2> 前置處理,將 `beanName` 標志為正在創建
                        beforeSingletonCreation(beanName);
                        try {
                            // <1.5.3> 對通過 FactoryBean 獲取的目標物件進行后置處理
                            // 遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            // <1.5.4> 后置處理,將 `beanName` 標志為不在創建中
                            afterSingletonCreation(beanName);
                        }
                    }
                    // <1.6> 如果快取中存在 `beanName` 對應的 FactoryBean 物件
                    // 上面不是判斷了嗎?也可能在上面的處理程序會有所變化,所以這里在做一層判斷
                    // 目的:快取 FactoryBean 創建的目標物件,則需要保證 FactoryBean 本身這個物件存在快取中
                    if (containsSingleton(beanName)) {
                        // <1.6.1> 將這個 FactoryBean 創建的目標物件保存至 `factoryBeanObjectCache`
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            // <1.7> 回傳 FactoryBean 創建的目標物件
            return object;
        }
    }
    // <2> `factory` 非單例模式,或單例 Bean 快取中不存在 `beanName` 對應的 FactoryBean 物件
    else {
        // <2.1> 則根據 `factory` 獲取目標物件,呼叫 FactoryBean#getObject() 方法
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        // <2.2> 是否需要后續處理,這個 FactoryBean 的前身 BeanDefinition 是否由 Spring 決議出來的,通常情況下都是
        if (shouldPostProcess) {
            try {
                // <2.2.1> 對通過 FactoryBean 獲取的目標物件進行后置處理
                // 遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        // <2.3> 回傳 FactoryBean 創建的目標物件,非單例模式不會進行快取
        return object;
    }
}

程序如下:

  1. factory 為單例模式,且單例 Bean 快取中存在 beanName 對應的 FactoryBean 物件

    1. 獲取單例鎖,保證安全

    2. factoryBeanObjectCache 快取中獲取 FactoryBean#getObject() 創建的目標物件

    3. 則根據 factory 獲取目標物件,呼叫 FactoryBean#getObject() 方法(反射機制)

    4. 這里再進行一次校驗(第 1.2 已經判斷過),看是否在快取中存在 FactoryBean 創建的目標物件,如果有則優先從快取中獲取,保證 FactoryBean#getObject() 只能被呼叫一次;沒有的話,則對付訓取到的目標物件進行接下來的處理

    5. 是否需要后續處理,這個 FactoryBean 的 BeanDefinition 是否由 Spring 決議出來的,通常情況下都是

      1. 若該 FactoryBean 處于創建中,則直接回傳這個目標物件,不進行接下來的處理程序

      2. 前置處理,將 beanName 標志為正在創建

      3. 對通過 FactoryBean 獲取的目標物件進行后置處理,遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)

      4. 后置處理,將 beanName 標志為不在創建中

      5. 如果快取中存在 beanName 對應的 FactoryBean 物件,上面不是判斷了嗎(第 1 步判斷過)?

        也可能在上面的處理程序會有所變化,所以這里在做一層判斷,目的:快取 FactoryBean 創建的目標物件,則需要保證 FactoryBean 本身這個物件存在快取中

        1. 將這個 FactoryBean 創建的目標物件保存至 factoryBeanObjectCache
      6. 回傳 FactoryBean 創建的目標物件

  2. factory 非單例模式,或單例 Bean 快取中不存在 beanName 對應的 FactoryBean 物件

    1. 則根據 factory 獲取目標物件,呼叫 FactoryBean#getObject() 方法(反射機制)
    2. 是否需要后續處理,這個 FactoryBean 的 BeanDefinition 是否由 Spring 決議出來的,通常情況下都是
      1. 對通過 FactoryBean 獲取的目標物件進行后置處理,遍歷所有的 BeanPostProcessor 的 postProcessAfterInitialization 方法(初始化的處理)
    3. 回傳 FactoryBean 創建的目標物件,非單例模式不會進行快取

概括:呼叫 FactoryBean#getObject() 獲取目標物件,單例模式會快取起來;程序中 Sping 考慮到各種情況,例如保證單例模式下 FactoryBean#getObject() 只呼叫一次,是否需要進行后置處理,

4. 非單例模式依賴檢查

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null && (curVal.equals(beanName) // 相等
            || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); // 包含
}

prototypesCurrentlyInCreation 中保存非單例模式下正在創建的 Bean 的名稱,這里又重新創建,表示出現回圈依賴,則直接拋出例外

Spring 對于非單例模式的 Bean 無法進行相關快取,也就無法處理回圈依賴的情況,選擇了直接拋出例外

5. BeanFactory 層次性加載 Bean 策略

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
BeanFactory parentBeanFactory = getParentBeanFactory();
// <5> 如果從當前容器中沒有找到對應的 BeanDefinition,則從父容器中加載(如果存在父容器)
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    // <5.1> 獲取 `beanName`,因為可能是別名,則進行處理
    // 和第 `1` 步不同,不需要對 `&` 進行處理,因為進入父容器重新依賴查找
    String nameToLookup = originalBeanName(name);
    // <5.2> 若為 AbstractBeanFactory 型別,委托父容器的 doGetBean 方法進行處理
    // 否則,就是非 Spring IoC 容器,根據引數呼叫相應的 `getBean(...)`方法
    if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
    }
    else if (args != null) {
        return (T) parentBeanFactory.getBean(nameToLookup, args);
    }
    else if (requiredType != null) {
        return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
    else {
        return (T) parentBeanFactory.getBean(nameToLookup);
    }
}

如果當前 BeanFactory 沒有對應的 BeanDefinition,也就無法創建 Bean,但是如果存在 BeanFactory,則將接下來的操作交由 BeanFactory,找不到會層層找上去,如果所有 BeanFactory 都找不到對應的 BeanDefinition 最侄訓拋出例外,

6. 將 beanName 標記為已創建

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
// <6> 如果不是僅僅做型別檢查,則表示需要創建 Bean,將 `beanName` 標記為已創建過
if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
}

如果不是僅僅做型別檢查,則呼叫 markBeanAsCreated(String beanName) 方法,如下:

// AbstractBeanFactory.java
protected void markBeanAsCreated(String beanName) {
    // 沒有創建
    if (!this.alreadyCreated.contains(beanName)) {
        // 加上全域鎖
        synchronized (this.mergedBeanDefinitions) {
            // 再次檢查一次:DCL 雙檢查模式
            if (!this.alreadyCreated.contains(beanName)) {
                // Let the bean definition get re-merged now that we're actually creating
                // the bean... just in case some of its metadata changed in the meantime.
                // 從 mergedBeanDefinitions 中洗掉 beanName,并在下次訪問時重新創建它
                clearMergedBeanDefinition(beanName);
                // 添加到已創建 bean 集合中
                this.alreadyCreated.add(beanName);
            }
        }
    }
}

將這個 beanName 保存在 alreadyCreated 集合中(SetFromMap),在后面的回圈依賴檢查中會使用到

7. 獲取 RootBeanDefinition

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
// <7> 從容器中獲取 `beanName` 對應的的 RootBeanDefinition(合并后)
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查是否為抽象類
checkMergedBeanDefinition(mbd, beanName, args);

因為我們定義的 Bean 大多數都被 Spring 決議成 GenericBeanDefinition 型別,具有父子關系,則需要獲取最終的 BeanDefinition;如果存在父子關系,則會進行一系列的合并,轉換成 RootBeanDefinition 物件,呼叫 getMergedLocalBeanDefinition(String beanName) 方法,如下:

// AbstractBeanFactory.java
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // Quick check on the concurrent map first, with minimal locking.
    // 從 `mergedBeanDefinitions` 快取中獲取合并后的 RootBeanDefinition,存在則直接回傳
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    if (mbd != null) {
        return mbd;
    }
    // 獲取 BeanDefinition 并轉換成,如果存在父子關系則進行合并
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
        throws BeanDefinitionStoreException {
    return getMergedBeanDefinition(beanName, bd, null);
}

protected RootBeanDefinition getMergedBeanDefinition(
        String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
        throws BeanDefinitionStoreException {

    // 加鎖
    synchronized (this.mergedBeanDefinitions) {
        RootBeanDefinition mbd = null;

        // Check with full lock now in order to enforce the same merged instance.
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }

        if (mbd == null) {
            // 如果沒有父類則直接轉換成 RootBeanDefinition 物件
            if (bd.getParentName() == null) {
                // Use copy of given root bean definition.
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            // 有父類則進行合并
            else {
                // Child bean definition: needs to be merged with parent.
                BeanDefinition pbd;
                try {
                    // 獲取父類的對應的 BeanDefinition 物件
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                        }
                        else {
                            throw new NoSuchBeanDefinitionException(parentBeanName,
                                    "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                    "': cannot be resolved without an AbstractBeanFactory parent");
                        }
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                            "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                }
                // Deep copy with overridden values.
                mbd = new RootBeanDefinition(pbd);
                // 父子合并
                mbd.overrideFrom(bd);
            }

            // Set default singleton scope, if not configured before.
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
            }

            // A bean contained in a non-singleton bean cannot be a singleton itself.
            // Let's correct this on the fly here, since this might be the result of
            // parent-child merging for the outer bean, in which case the original inner bean
            // definition will not have inherited the merged outer bean's singleton status.
            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                mbd.setScope(containingBd.getScope());
            }

            // Cache the merged bean definition for the time being
            // (it might still get re-merged later on in order to pick up metadata changes)
            if (containingBd == null && isCacheBeanMetadata()) {
                // 放入快取中
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }

        return mbd;
    }
}

程序大致如下:

  1. mergedBeanDefinitions 快取中獲取合并后的 RootBeanDefinition,存在則直接回傳,不存在則進行后面的操作
  2. 獲取合并后的 RootBeanDefinition 物件,邏輯并不復雜,將一些屬性進行合并;這里對于 BeanDefinition 的獲取也存在層次性查找策略;注意,如果一個單例 BeanDefinition 包含在非單例 BeanDefinition,那么會變成非單例 Bean

后續還會對合并后的 RootBeanDefinition 物件進行檢查,如果是抽象的,則拋出例外

8. 依賴 Bean 的處理

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
// Guarantee initialization of beans that the current bean depends on.
// <8> 獲取當前正在創建的 Bean 所依賴物件集合(`depends-on` 配置的依賴)
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
    for (String dep : dependsOn) {
        // <8.1> 檢測是否存在回圈依賴,存在則拋出例外
        if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
        }
        // <8.2> 將 `beanName` 與 `dep` 之間依賴的關系進行快取
        registerDependentBean(dep, beanName);
        try {
            // <8.3> 先創建好依賴的 Bean(重新呼叫 `getBean(...)` 方法)
            getBean(dep);
        }
        catch (NoSuchBeanDefinitionException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
        }
    }
}
  • 每個 Bean 不一定是單獨作業的,可以通過 depends-on 配置依賴的 Bean,其他 Bean 也可以依賴它

  • 對于依賴的 Bean,會優先加載,所以在 Spring 的加載順序中,在初始化某個 Bean 的時候,首先會初始化這個 Bean 的依賴

isDependent 方法

在初始化依賴的 Bean 之前,會呼叫 isDependent(String beanName, String dependentBeanName) 方法,判斷是否出現回圈依賴,方法如下:

DefaultSingletonBeanRegistry.java
protected boolean isDependent(String beanName, String dependentBeanName) {
    synchronized (this.dependentBeanMap) {
        return isDependent(beanName, dependentBeanName, null);
    }
}

private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
    // <1> `alreadySeen` 中已經檢測過該 `beanName` 則直接回傳 `false`
    if (alreadySeen != null && alreadySeen.contains(beanName)) {
        return false;
    }
    // <2> 獲取最終的 `beanName`,因為可能是別名,需要進行相關處理
    String canonicalName = canonicalName(beanName);
    // <3> 從 `dependentBeanMap` 中獲取依賴 `beanName` 的 Bean 集合
    Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
    // <4> 沒有 Bean 依賴該 `beanName`,也就不存在回圈依賴,回傳 `false`
    if (dependentBeans == null) {
        return false;
    }
    // <5> 依賴 `beanName` 的 Bean 們包含 `dependentBeanName`,表示出現回圈依賴,回傳 `true`
    if (dependentBeans.contains(dependentBeanName)) {
        // `beanName` 與 `dependentBeanName` 相互依賴
        return true;
    }
    // <6> 對依賴該 `beanName` 的 Bean 們進行檢查,看它們是否與 `dependentBeanName` 存在依賴,遞回處理
    for (String transitiveDependency : dependentBeans) {
        if (alreadySeen == null) {
            alreadySeen = new HashSet<>();
        }
        alreadySeen.add(beanName);
        if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
            return true;
        }
    }
    return false;
}

程序大致如下:

  1. alreadySeen 中已經檢測過該 beanName 則直接回傳 false
  2. 獲取最終的 beanName,因為可能是別名,需要進行相關處理
  3. dependentBeanMap 中獲取依賴 beanName 的 Bean 集合
  4. 沒有 Bean 依賴該 beanName,也就不存在回圈依賴,回傳 false
  5. 依賴 beanName 的 Bean 們包含 dependentBeanName,表示出現回圈依賴,回傳 true
  6. 對依賴該 beanName 的 Bean 們進行檢查,看它們是否與 dependentBeanName 存在依賴,遞回處理

判斷是否出現回圈依賴的程序有點繞,需要花點時間理解一下,例如:現在檢查 A ->(依賴)B,看是否出現回圈依賴,我獲取到依賴 A 的所有 Bean,看 B 是否依賴這里面的 Bean,如果出現 A -> B -> C -> A,那就出現回圈依賴了,如果出現回圈依賴,則會拋出例外,所以我們說 Spring 處理了單例 Bean 的回圈依賴注入比較好一點,

registerDependentBean 方法

beanNamedepbeanName 的依賴)之間依賴的關系進行快取,呼叫 registerDependentBean(String beanName, String dependentBeanName) 方法,如下:

DefaultSingletonBeanRegistry.java
public void registerDependentBean(String beanName, String dependentBeanName) {
    String canonicalName = canonicalName(beanName);

    // 對應關系:beanName -> 依賴 beanName 的集合
    synchronized (this.dependentBeanMap) {
        Set<String> dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, 
                                                                           k -> new LinkedHashSet<>(8));
        if (!dependentBeans.add(dependentBeanName)) {
            return;
        }
    }

    // 對應關系:beanName - > beanName 的依賴的集合
    synchronized (this.dependenciesForBeanMap) {
        Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, 
                                                                                      k -> new LinkedHashSet<>(8));
        dependenciesForBean.add(canonicalName);
    }
}

將兩者的依賴關系保存起來,目的是在 isDependent 方法中判斷是否出現回圈依賴

getBean 方法

加載 beanName 依賴的 Bean,同樣是呼叫 AbstractBeanFactory#getBean(String dep) 方法,也就是本文開頭講的這個方法

9. 不同作用域的 Bean 的創建

Spring 的作用域劃分為三種:單例模式、原型模式、其他模式,會依次進行判斷,然后進行創建,創建程序都是一樣的,主要是存盤范圍不一樣

  • 單例模式:一個 BeanFactory 有且僅有一個實體
  • 原型模式:每次依賴查找和依賴注入生成新 Bean 物件
  • 其他模式,例如 request 作用域會將 Bean 存盤在 ServletRequest 背景關系中;session 作用域會將 Bean 存盤在 HttpSession 中;application 作用域會將 Bean 存盤在 ServletContext 中

單例模式

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
if (mbd.isSingleton()) { // <9.1> 單例模式
    /*
     * <9.1.1> 創建 Bean,成功創建則進行快取,并移除快取的早期物件
     * 創建程序實際呼叫的下面這個 `createBean(...)` 方法
     */
    sharedInstance = getSingleton(beanName,
            // ObjectFactory 實作類
            () -> {
                try {
                    // **【核心】** 創建 Bean
                    return createBean(beanName, mbd, args);
                } catch (BeansException ex) {
                    // Explicitly remove instance from singleton cache: It might have been put there
                    // eagerly by the creation process, to allow for circular reference resolution.
                    // Also remove any beans that received a temporary reference to the bean.
                    // 如果創建程序出現例外,則顯式地從快取中洗掉當前 Bean 相關資訊
                    // 在單例模式下為了解決回圈依賴,創建程序會快取早期物件,這里需要進行洗掉
                    destroySingleton(beanName);
                    throw ex;
                }
    });
    // <9.1.2> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
    // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

如果是單例模式,創建程序大致如下:

  1. 呼叫 DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法

    創建 Bean,成功創建則進行快取,并移除快取的早期物件,創建程序實際呼叫的下面這個 AbstractAutowireCapableBeanFactory#createBean(...) 方法

  2. FactoryBean 的處理,在前面 3. FactoryBean 的處理 中已經分析過

getSingleton 方法

DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法,單例模式下獲取單例 Bean,如下:

// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 全域加鎖
    synchronized (this.singletonObjects) {
        // <1> 從 `singletonObjects` 單例 Bean 的快取中獲取 Bean(再檢查一遍),存在則直接回傳,否則開始創建
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                        "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            // <2> 將 `beanName` 標記為單例模式正在創建
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                /**
                 * <3> 創建 Bean,實際呼叫 
                 * {@link AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])} 方法
                 */
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // <4> 將 `beanName` 標記為不在創建中,照應第 `2` 步
                afterSingletonCreation(beanName);
            }
            // <5> 如果這里是新創建的單例模式 Bean,則在 `singletonObjects` 中進行快取(無序),移除快取的早期物件
            // 并在 `registeredSingletons` 中保存 `beanName`,保證注冊順序
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

程序大致如下:

  1. singletonObjects 單例 Bean 的快取中獲取 Bean(再檢查一遍),存在則直接回傳,否則開始創建
  2. beanName 標記為單例模式正在創建
  3. 【核心】創建 Bean,實際呼叫 AbstractAutowireCapableBeanFactory#createBean(...) 方法
  4. beanName 標記為不在創建中,照應第 2
  5. 如果這里是新創建的單例模式 Bean,則在 singletonObjects 中進行快取(無序),移除快取的早期物件,并在 registeredSingletons 中保存 beanName,保證注冊順序
createBean 方法

AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 方法,創建 Bean,整個程序大致如下:

  1. Bean 的實體化
  2. 屬性賦值(包括依賴注入)
  3. Aware 介面回呼
  4. 呼叫初始化方法

上面涉及到 Bean 生命周期的大部分階段,將會在后續的文章中依次分析

原型模式

對應代碼段:

// AbstractBeanFactory.java
// <9.2> 原型模式
else if (mbd.isPrototype()) {
    // It's a prototype -> create a new instance.
    Object prototypeInstance = null;
    try {
        // <9.2.1> 將 `beanName` 標記為**非單例模式**正在創建
        beforePrototypeCreation(beanName);
        // <9.2.2> **【核心】** 創建 Bean
        prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
        // <9.2.3> 將 `beanName` 標記為不在創建中,照應第 `9.2.1` 步
        afterPrototypeCreation(beanName);
    }
    // <9.2.4> 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
    // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

程序大致如下:

  1. beanName 標記為非單例模式正在創建
  2. 【核心】創建 Bean 也是呼叫 AbstractAutowireCapableBeanFactory#createBean(...) 方法,這里沒有快取,每次加載 Bean 都會創建一個物件
  3. beanName 標記為不在創建中,照應第 1
  4. FactoryBean 的處理,在前面 3. FactoryBean 的處理 中已經分析過

其他模式

對應代碼段:

// AbstractBeanFactory.java
// <9.3> 其他模式
else {
    // <9.3.1> 獲取該模式的 Scope 物件 `scope`,不存在則拋出例外
    String scopeName = mbd.getScope();
    final Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {
        // <9.3.2> 從 `scope` 中獲取 `beanName` 對應的物件(看你的具體實作),不存在則執行**原型模式**的四個步驟進行創建
        Object scopedInstance = scope.get(beanName, () -> {
            // 將 `beanName` 標記為**非單例模式**式正在創建
            beforePrototypeCreation(beanName);
            try {
                // **【核心】** 創建 Bean
                return createBean(beanName, mbd, args);
            }
            finally {
                // 將 `beanName` 標記為不在創建中,照應上一步
                afterPrototypeCreation(beanName);
            }
        });
        // 獲取 Bean 的目標物件,`scopedInstance` 非 FactoryBean 型別直接回傳
        // 否則,呼叫 FactoryBean#getObject() 獲取目標物件
        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    catch (IllegalStateException ex) {
        throw new BeanCreationException(beanName,
                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                ex);
    }
}

程序如下:

  1. 獲取該模式的 Scope 物件 scope,不存在則拋出例外
  2. scope 中獲取 beanName 對應的物件(看你的具體實作),不存在則執行原型模式的四個步驟進行創建

想要自定義一個作用域,可以實作 org.springframework.beans.factory.config.Scope 介面,并往 Spring 應用背景關系注冊即可

10. 型別轉換

對應代碼段:

// AbstractBeanFactory#doGetBean(...) 方法
// <10> 如果入參 `requiredType` 不為空,并且 Bean 不是該型別,則需要進行型別轉換
if (requiredType != null && !requiredType.isInstance(bean)) {
    try {
        // <10.1> 通過型別轉換機制,將 Bean 轉換成 `requiredType` 型別
        T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
        // <10.2> 轉換后的 Bean 為空則拋出例外
        if (convertedBean == null) {
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
        // <10.3> 回傳型別轉換后的 Bean 物件
        return convertedBean;
    }
    catch (TypeMismatchException ex) {
        if (logger.isTraceEnabled()) {
            logger.trace("Failed to convert bean '" + name + "' to required type '" +
                    ClassUtils.getQualifiedName(requiredType) + "'", ex);
        }
        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    }
}

如果入參 requiredType 不為空,并且 Bean 不是該型別,則需要進行型別轉換,程序如下:

  1. 通過型別轉換機制,將 Bean 轉換成 requiredType 型別,對 Spring 的型別轉換機制感興趣的小伙伴可以自己研究,參考 org.springframework.core.convert.support.DefaultConversionService
  2. 轉換后的 Bean 為空則拋出例外
  3. 回傳型別轉換后的 Bean 物件

總結

本文對 BeanFactory 介面的體系結構進行了分析,得知 DefaultListableBeanFactory 是 BeanFactory 的最底層實作,也就是 Spring 的底層 IoC 容器,接著分析了 AbstractBeanFactorygetBean(...) 方法,當我們顯示或者隱式地呼叫這個方法時,會觸發 Bean 的加載,上面所有小節對 Bean 的加載程序進行了分析,我已經有序地在每個小節面前添加了序號,這些序號對應著加載程序中的順序,

不同作用域的 Bean 的創建,底層都會呼叫 AbstractAutowireCapableBeanFactorycreateBean(...) 方法進行創建,創建 Bean 的程序涉及到 Bean 生命周期的大部分階段,例如實體化階段、屬性賦值階段、Aware 介面回呼階段、初始化階段都是在這個方法中完成的,整個創建程序將在后續的文章進行分析,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/265019.html

標籤:Java

上一篇:使用Prometheus搞定微服務監控

下一篇:JUnit5學習之八:綜合進階(終篇)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more