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

月圓吖發表於2021-03-01

該系列文章是本人在學習 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 介面的繼承關係

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

簡單描述這些介面:

  • 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 的功能

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

死磕Spring之IoC篇 - 開啟 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 介面回撥階段、初始化階段都是在這個方法中完成的,整個建立過程將在後續的文章進行分析。

相關文章