天天看點

Spring源碼學習筆記——Bean加載

快速入門:狂神講的Spring教程,這個很适合剛入門,想快速過一遍Spring架構的小夥伴!

加強鞏固:尚矽谷-Spring5架構最新版教程,尚矽谷的教程品質一向很不錯,非常全面,也适合初學架構的新手!

學完架構,自己多練習使用,隻有熟悉使用了,看源碼才不那麼迷茫!切不可一味的堆積課程進度快餐式學習,要反複消化哦~

下面進入正題:

分析源碼離不開注釋,先搭建起來Spring源碼閱讀環境:超詳細圖解!教小白學妹基于IDEA+Gradle+jdk11搭建Spring架構源碼閱讀環境

本文主要内容參考《Spring源碼深度解析》這本書,以及一些技術部落格分享。

Spring源碼系列文章肝了整整2個月,希望大家三連支援一下!

Spring源碼學習筆記——Bean加載
Spring源碼學習筆記——Bean加載

Spring源碼分析——Bean的加載

0. 前言引入

熟悉Spring架構的小夥伴都知道,Spring有兩大核心子產品:IOC (控制反轉 ) 和 AOP (面向切面程式設計)。對于Spring IOC,我們又通常将其稱為 IOC 容器,IOC 的2個實作方式分别為依賴注入(DI)和依賴查找(DL)。

注:由于依賴查找(DL)使用的很少,是以 IOC 也被叫做依賴注入。

IOC 和 DI 、DL 的關系圖:

Spring源碼學習筆記——Bean加載
Spring源碼學習筆記——Bean加載

Spring IOC 實作了依賴注入,通過一個核心的 Bean 工廠 (BeanFactory) 來負責各個 Bean 的執行個體化和依賴管理。各個 Bean 不需要考慮各自複雜的建立過程,進而實作解耦。

對于 IOC 來說,最重要的概念就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依賴注入。

Spring 作者 Rod Johnson 設計了兩個接口用以表示容器:

BeanFactory

BeanFactory 粗暴簡單,可以了解為就是個 HashMap結構,Key 是 BeanName,Value 是 Bean 執行個體。通常隻提供注冊(put),擷取(get)這兩個功能。我們可以稱之為 “低級容器”。

ApplicationContext

ApplicationContext 可以稱之為 “進階容器”。因為他比 BeanFactory 多了更多的功能。他繼承了多個接口。是以具備了更多的功能。例如資源的擷取,支援多種消息(例如 JSP tag 的支援),對 BeanFactory 多了工具級别的支援等待。是以你看他的名字,已經不是 BeanFactory 之類的工廠了,而是 “應用上下文”, 代表着整個大容器的所有功能。該接口定義了一個 refresh 方法,此方法是所有閱讀 Spring 源碼的人的最熟悉的方法,用于重新整理整個容器,即重新加載/重新整理所有的 Bean。

我們通過UML圖來看一下BeanFactory與ApplicationContext的關系:

Spring源碼學習筆記——Bean加載
Spring源碼學習筆記——Bean加載

從UML關系圖中,也可以看出ApplicationContext肯定會比BeanFactory更加複雜。在之後 Spring源碼分析——容器擴充中會詳細分析(努力更新中)~

如果對Spring IOC的功能進行粗略概括的話,其主要分為如下2個功能點:

XML标簽的解析與加載(也可以是注解),讀取 XML 配置檔案,将XML配置檔案中的标簽資訊解析為 BeanDefinition 的形式(即,從配置檔案或者注解中擷取 Bean 的定義資訊解析為 BeanDefinition 對象,并為其注冊一些擴充功能)。

—> BeanDefinition :XML标簽解析,總體來說主要就是完成對預設标簽的4個标簽進行解析,即:标簽、标簽、标簽(最為複雜)、标簽。其中的過程彎彎繞繞,不過我們隻要清楚,目的是将XML 配置檔案中的配置轉換為 BeanDefinition 對象。

@Bean —> BeanDefinition

Bean加載,通過XML解析後得到的Bean 的定義資訊( BeanDefinition )擷取 Bean 對象執行個體。

BeanDefinition —> Bean對象

此外,IOC 還具有:自動裝配、支援集合、指定初始化方法和銷毀方法等功能。

如下圖所示:

Spring源碼學習筆記——Bean加載
Spring源碼學習筆記——Bean加載

本文主要是對Bean加載這一流程進行解析,而XML标簽的解析加載,這裡不作為重點!

1. FactoryBean接口和BeanFactory接口

我們先來分析一下在Spring Bean加載中,常用的2個工廠接口:

BeanFactory 是 Bean 的工廠,使用簡單工廠模式, ApplicationContext 的父類,IOC 容器的核心(容器頂級接口),負責生産、執行個體化、配置Bean對象以及建立這些Bean對象間的依賴。BeanFactory 執行個體化後并不會自動執行個體化 Bean,隻有當 Bean 被使用時才執行個體化與裝配依賴關系,屬于延遲加載,适合多例模式。

FactoryBean 是 Bean(工廠 Bean),使用了工廠方法模式,作用是生産其他 Bean 執行個體,可以通過實作該接口,提供一個工廠方法來自定義執行個體化 Bean 的邏輯。FactoryBean 接口由 BeanFactory 中配置的對象實作,這些對象本身就是用于建立對象的工廠,如果一個 Bean 實作了這個接口,那麼它就是建立對象的工廠 Bean,而不是 Bean 執行個體本身。

1.1 FactoryBean接口

FactoryBean接口中定義了三個方法:

public interface FactoryBean<T> {
    // 擷取由FactoryBean建立的Bean執行個體:
    @Nullable
    T getObject() throws Exception;

    // 獲得FactoryBean建立的Bean的類型:
    @Nullable
    Class<?> getObjectType();

    // 判斷Bean執行個體的作用域是否是單例Singleton,是則傳回true,否則傳回false
    // 如果該執行個體是單例模式的,則該Bean執行個體會放到Spring容器的單例緩存池中:
    default boolean isSingleton() {
        return true;
    }
}
      

1.2 BeanFactory接口

BeanFactory接口中定義了如下一些方法:

public interface BeanFactory {
    // 如果Bean執行個體對象對應的beanName開頭帶有&符号,說明是一個FactoryBean類型的對象
    String FACTORY_BEAN_PREFIX = "&";
    
    // 根據beanName從BeanFactory中擷取Bean對象
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    
    // 從BeanFactory中擷取指定類型(requiredType)的Bean對象
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    
    // 判斷BeanFactory中是否包含指定beanName的Bean對象
    boolean containsBean(String name);
    
    // 目前Bean對象是否是單例Singleton
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    // 目前Bean對象是否是Prototyp模式
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    
    oolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    // 根據beanName擷取Bean對象的類型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
    
    // 擷取指定Bean的别名清單
    String[] getAliases(String name);
}
      

1.3 基本使用案例

首先建立一個實體類Student:

public class Student {

   private int age;
   private String name;
   private String school;

   // setter/getter方法省略
}
      

下面建立一個用來作為Bean對象的類,令其實作 

FactoryBean

 接口:

public class StudentFactoryBean implements FactoryBean<Student> {

   private String studentInfo;

   public String getStudentInfo() {
      return studentInfo;
   }

   public void setStudentInfo(String studentInfo) {
      this.studentInfo = studentInfo;
   }

   @Override
   public Student getObject() throws Exception {
      Student stu = new Student();
      String[] splitInfo = studentInfo.split(",");
      stu.setAge(Integer.valueOf(splitInfo[0]));
      stu.setName(splitInfo[1]);
      stu.setSchool(splitInfo[2]);
      return stu;
   }

   @Override
   public Class<Student> getObjectType() {
      return Student.class;
   }

   @Override
   public boolean isSingleton() {
      return false;
   }
}
      

上面代碼中,最核心的就是

T getObject()

 方法的重寫!下面我們在XML配置檔案中将其注入Spring容器:

<bean id="studentFactoryBean" class="top.csp1999.StudentFactoryBean">
   <property name="studentInfo" value="18,csp,Haust" />
</bean>
      

2. Bean加載總體流程概述

先來看一個Demo:

聲明2個要注冊到IOC中的對象

ComponentA

ComponentB

public class ComponentA {
}
      
public class ComponentB {
}
      

測試從IOC中擷取這兩個Bean對象:

public class BeanFactoryTest {
    public static void main(String[] args) {

        // 加載與解析XML配置檔案,獲得BeanFactory:
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bf.xml"));

        // 從BeanFactory中擷取Bean對象
        Object a = beanFactory.getBean("componentA");
        Object b = beanFactory.getBean("componentB");

        System.out.println(a);// com.myspring.test.xmltest.ComponentA@1c93084c
        System.out.println(b);// com.myspring.test.xmltest.ComponentB@6ef888f6
    }
}
      

由上面的Demo我們可以知道,Spring通過調用 

BeanFactory

 的 

getBean()

 方法來加載 Bean,那麼我們進入 

AbstractBeanFactory

 來看一下源碼:

/**
 * 根據參數name,requiredType,args擷取Bean對象執行個體:
 *
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型 -> 擷取Bean對象時,不僅要根據name去檢索,還要判斷Bean的類型是否一緻
 * @param args 這個參數用到再做分析
 * @return 該方法傳回目标Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
        throws BeansException {
    // 調用真正去擷取Bean對象的方法:
    // 注:Spring架構源碼的命名規範非常嚴謹,doXxx方法(内層方法)内封裝的是具體執行邏輯的代碼,而調用doXxx的方法是其外層方法
    return doGetBean(name, requiredType, args, false);
}

/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    // transformedBeanName(name)方法:根據傳入的name參數,擷取真正的Bean對應的beanName,什麼意思呢?
    // Spring中管理的Beand對象是可以指定設定别名的,Spring Bean設定别名的兩種方式:參考 https://blog.csdn.net/qq_34129814/article/details/7
    // 參數傳進來的name,有可能是一個别名(eg: alias設定的别名),也有可能是一個&開頭的name(解釋如下):
    // (1)别名name(eg: alias設定的别名),transformedBeanName(name)方法就是通過别名重定向出來真實beanName名稱
    // (2)&開頭的name,說明,你要擷取的Bean執行個體對象,是一個FactoryBean對象。
    // FactoryBean:如果某個Bean的配置非常複雜,使用Spring管理不容易、不夠靈活,想要使用編碼的形式去建構它,
    // 那麼你就可以提供一個建構該Bean執行個體的工廠,這個工廠就是FactoryBean接口實作類。FactoryBean接口實作類還是需要使用Spring管理的。
    // 這裡就涉及到兩種對象,一種是FactoryBean接口實作類(IOC管理的),另一個就是FactoryBean接口内部管理的對象。
    // 如果要拿FactoryBean接口實作類,使用getBean時傳的beanName需要帶“&”開頭。
    // 如果你要FactoryBean内部管理的對象,你直接傳beanName不需要帶“&”開頭。
    String beanName = transformedBeanName(name);
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    // 根據transformedBeanName方法轉換後的真實beanName,直接嘗試從緩存中擷取Bean的共享單執行個體(單例):
    // 注:
    // 第一個getSingleton(String beanName)方法是一個參數的,
    // 後面還有一個重載的getSingleton方法(2個參數),2個不要搞混了!
    Object sharedInstance = getSingleton(beanName);
    // CASE1:
    // 如果緩存中有對應的資料,此時緩存資料可能是普通單執行個體,也可能是 FactoryBean,是以需要根據name來進行判斷
    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 + "'");
            }
        }
        // 這裡為什麼又要套呢?為啥不直接拿回去用呢?
        // 其實,你從IOC中拿到的對象,它可能是普通單執行個體,也可能是FactoryBean執行個體。
        // 如果是FactoryBean執行個體,這個時候還要進行處理。主要是看name是帶“&” 還是不帶“&”,
        // 帶“&”:則說明這次getBean方法想要拿FactoryBean對象。
        // 不帶“&”:則說明是要拿FactoryBean内部管理的執行個體。
        /**
         * 擷取給定Bean執行個體的對象,如果是FactoryBean類型,則可以是該執行個體本身或其建立的子Bean對象。
         * 方法參數:
         * sharedInstance: 緩存中拿到的單執行個體對象
         * name: 未處理“&”的name
         * beanName: 處理過“&”和别名後的name
         * mbd: 合并過後的bd(BeanDefinition)資訊。
         */
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根據beanName從緩存中沒有找到對應的資料,那麼我們就需要自己建立了...
    else {
        // 一、原型循環依賴問題判定:建立Bean時,判斷是否出現循環依賴的情況
        // 舉個例子:
        // prototypeA -> B, B -> prototypeA
        // 1.會向正在建立中的原型集合内添加一個字元串 “A”
        // 2.建立prototypeA對象,隻是一個早期對象。
        // 3.處理prototypeA的依賴,發現A依賴了B類型的對象
        // 4.觸發了Spring.getBean(“B”)的操作。
        // 5.根據B的構造方法反射建立出來了B的早期執行個體
        // 6.Spring處理B對象的依賴,發現依賴了A。
        // 7.Spring轉頭回來再次去擷取A去了。getBean(“A”).
        // 8.條件會傳回true,最終抛出異常,算是結束了循環依賴注入。
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        // Check if bean definition exists in this factory.
        // 如果beanDefinitionMap中也就是已經加載的類中不包括beanName則嘗試從parentBeanFactory中檢測
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                        nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                // Delegation to parent with explicit args.
                // 遞歸到BeanFactory中尋找
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }
        // 如果不僅僅是做類型檢查則是建立Bean,這裡要進行記錄
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }
        StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
                .tag("beanName", name);
        try {
            if (requiredType != null) {
                beanCreation.tag("beanType", requiredType::toString);
            }
            // 二、擷取合并BD資訊
            // 将存儲XML配置檔案的GenericBeanDefinition轉換為RootBeanDefinition,
            // 如果指定BeanName是子Bean的話同時會合并父類的相關屬性
            // 為什麼需要合并呀?因為BD支援繼承
            // BD: 在XML配置檔案中用 parent 屬性可以定義父 <bean> 和子 <bean> ,父 <bean> 用 RootBeanDefinition表示
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 判斷目前BD是否為抽象BD,抽象BD不能建立執行個體,隻能作為父BD讓子BD去繼承。
            checkMergedBeanDefinition(mbd, beanName, args);
            // 三、depends-on屬性處理..
            // <bean name="A" depends-on="B" ... />
            // <bean name="B" .../>
            // 循環依賴問題
            // <bean name="A" depends-on="B" ... />
            // <bean name="B" depends-on="A" .../>
            // Spring是處理不了這種情況的,需要報錯..
            // Spring需要發現這種情況的産生。
            // 怎麼發現呢? 依靠兩個Map,一個map是 dependentBeanMap 另一個是 dependenciesForBeanMap
            // 1. dependentBeanMap 記錄依賴目前beanName的其他beanName
            // 2. dependenciesForBeanMap 記錄目前beanName依賴的其它beanName集合
            String[] dependsOn = mbd.getDependsOn();
            // 若存在依賴則需要遞歸執行個體化依賴的bean
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // 判斷循環依賴..
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'
                    }
                    // 假設<bean name="A" depends-on="B" ... />
                    // dep:B,beanName:A
                    // 以B為視角 dependentBeanMap {"B":{"A"}}
                    // 以A為視角 dependenciesForBeanMap {"A" :{"B"}}
                    // 緩存依賴調用
                    registerDependentBean(dep, beanName);
                    try {
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }
            // CASE-SINGLETON:Create bean instance.
            // 建立單執行個體的邏輯
            // 執行個體化依賴的Bean後便可以執行個體化mbd本身了(singleton模式的建立)
            if (mbd.isSingleton()) {
                // 第二個getSingleton方法,這個方法更傾向于建立執行個體并傳回:
                sharedInstance = getSingleton(beanName, () -> {
                    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.
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                //這 裡為啥不直接傳回,還調用getObjectForBeanInstance(...)?
                // 這裡為什麼又要套呢?為啥不直接拿回去用呢?
                // 其實,你從IOC中拿到的對象,它可能是普通單執行個體,也可能是FactoryBean執行個體。
                // 如果是FactoryBean執行個體,這個時候還要進行處理。主要是看name是帶“&” 還是 不帶“&”,
                // 帶“&”說明這次getBean想要拿FactoryBean對象。
                // 否則是要拿FactoryBean内部管理的執行個體。
                beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // CASE-PROTOTYPE: 建立多執行個體
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                // prototype原型模式的建立(new)
                Object prototypeInstance = null;
                try {
                    // 記錄目前線程相關的正在建立的原型對象beanName
                    beforePrototypeCreation(beanName);
                    // createBean方法建立對象
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    // 從正在建立中的集合中移除beanName對應的Bean。
                    afterPrototypeCreation(beanName);
                }
                beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            // CASE-OTHER: 這一情況筆記複雜,不做分析!
            else {
                // 指定的scope上執行個體化bean
                String scopeName = mbd.getScope();
                if (!StringUtils.hasLength(scopeName)) {
                    throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
                }
                Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new ScopeNotActiveException(beanName, scopeName, ex);
                }
            }
        }
        catch (BeansException ex) {
            beanCreation.tag("exception", ex.getClass().toString());
            beanCreation.tag("message", String.valueOf(ex.getMessage()));
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
        finally {
            beanCreation.end();
        }
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}
      

注意:從上述代碼中,可以看到擷取Bean對象執行個體的方法是由getBean()外層方法去調用doGetBean()内層方法,這種嵌套結構在Spring架構源碼中非常常見。

通過上面的注釋,大緻了解到Spring加載 Bean 的過程,下面通過流程圖再鞏固一下(圖檔出自其他文章,文章末尾會聲明出處):

Spring源碼學習筆記——Bean加載
Spring源碼學習筆記——Bean加載

上面是跟蹤了 getBean 的調用鍊建立的流程圖,為了能夠很好地了解 Bean 加載流程,省略一些異常、日志和分支處理和一些特殊條件的判斷。

從上面的流程圖中,可以看到一個 Bean 加載會經曆這麼幾個階段(用綠色标記):

擷取 BeanName,對傳入的 name 進行解析,轉化為可以從 Map 中擷取到 BeanDefinition 的 beanName。

合并 Bean 定義,對父類的定義進行合并和覆寫,如果父類還有父類,會進行遞歸合并,以擷取完整的 Bean 定義資訊。

執行個體化,使用構造或者工廠方法建立 Bean 執行個體。

屬性填充,尋找并且注入依賴,依賴的 Bean 還會遞歸調用 getBean 方法擷取。

初始化,調用自定義的初始化方法。

擷取最終的 Bean,如果是 FactoryBean 需要調用getObject 方法,如果需要類型轉換調用 TypeConverter 進行轉化。

3. doGetBean方法詳細解析(上)

3.1 beanName的轉換方法

transformedBeanName(name)該方法的作用是,根據傳入的 name 參數,擷取真正的 Bean 對應的 beanName。該方法的 name 參數,有可能是一個别名(alias 屬性設定的别名),也有可能是一個&開頭的 name:

① 别名name(alias 屬性設定的别名),transformedBeanName(name)方法就是通過别名重定向出來真實beanName 名稱的!Spring Bean設定别名的兩種方式如下:

<!-- 
    方式一: 
    *alias* 屬性設定别名, 使用alias設定别名,alias的 name 要和 bean 的 ID 相同, 可以設定多個别名!
-->
<bean id="addOne" class="...">
    <alias name="addOne" alias="add"/>
    <alias name="addOne" alias="doAdd"/>
</bean>
   
<!-- 
    方式二: 
    *name* 屬性設定别名, 多個别名用逗号隔開!
-->
<bean id="addOne" class="..." name="add,doAdd"">
</bean>
      

② 

&

 符号開頭的 name ,說明,你要擷取的 Bean 執行個體對象,是一個FactoryBean對象。

getBean("&beanName");// 這種情況擷取的就是一個 FactoryBean 對象
      

擷取真正的beanName的步驟如下:

  • 去除

    FactoryBean

    的修飾符,一般就是去除 name 參數的

    &

    字首;
  • 取指定的 alias 所表示的最終 beanName,例如别名 A 指向别名 B,别名 B 指向名稱為 C 的 Bean,則最後傳回 C。
/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    // 根據傳入的name參數,擷取真正的Bean對應的beanName
    String beanName = transformedBeanName(name);
    
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    ...
}
      

我們跟進

transformedBeanName(name);

方法内部檢視:

/**
 * Return the bean name, stripping out the factory dereference prefix if necessary,
 * and resolving aliases to canonical names.
 * (翻譯:傳回Bean的真實名稱,必要時去除工廠引用字首,并将别名解析為規範名稱)
 *
 * 根據傳入的name參數(該參數可能是别名,也可能是&開頭的name),擷取真正的Bean對應的beanName:
 *
 * @param name 擷取Bean對象時,要傳入的name參數(該參數可能是别名,也可能是&開頭的name)
 * @return 傳回轉換後真正的Bean名稱(beanName)
 */
protected String transformedBeanName(String name) {
    // 1.BeanFactoryUtils.transformedBeanName(name) 調用工廠工具類的轉換方法:
    // 根據傳入的name參數去擷取轉換後的name參數(去掉"&"符号):eg -> name="&abc" 則轉換後獲得 name="abc"
    
    // 2.canonicalName方法 -> 根據Bean的别名name,擷取真實的beanName:
    // BeanFactoryUtils.transformedBeanName(name)方法轉換後的name是最終值麼?
    // 不是!BeanFactoryUtils.transformedBeanName(name)方法僅僅是去掉name參數的"&"符号而已!
    // 轉換後得到的name還有可能是alias屬性指定的别名:
    // aliasMap儲存别名資訊:
    // {"C":"B", "B":"A"} A有一個别名叫做“B”,但是别名“B” 它又被别名了,它有一個别名叫做“C”
    // 假設調用getBean(name)方法時,name傳的是“C”,那最終要得到什麼? 要得到“A”(真正的beanName),而不是"B"!
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
      

BeanFactoryUtils.transformedBeanName(name)

方法去除 name 參數中的 

FactoryBean

 的修飾符

&

/**
 * Return the actual bean name, stripping out the factory dereference
 * prefix (if any, also stripping repeated factory prefixes if found).
 * (翻譯:傳回實際的bean名稱,删除工廠取消引用字首(如果有的話,還删除重複的工廠字首(如果找到)))
 * 
 * 根據傳入的name參數去擷取轉換後的name參數(去掉"&"符号):eg -> name="&abc" 則轉換後獲得 name="abc"
 * @param name 擷取Bean對象時,要傳入的name參數(該參數可能是别名,也可能是&開頭的name)
 * @return 傳回轉換後真正的Bean名稱(beanName)
 * @see BeanFactory#FACTORY_BEAN_PREFIX
 */
// 位于BeanFactoryUtils工具類中的方法
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    // 如果條件成立:說明調用getBean方法時傳的參數name,不是“&”符号開頭的,是以直接傳回name即可
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    // 執行到這裡,說明name一定是“&”符号開頭的,說明這次getBean方法就是要拿封裝為Bean的FactoryBean執行個體對象(有點套娃的感覺)!
    // 調用transformedBeanNameCache方法将去掉“&”符号得到的結果緩存起來。提高後續程式運作性能的。
    // 科普一下:map.computeIfAbsent(key, value)方法
    // 當map中對應的key為null或者 key->value是null時,這次put操作就會成功,寫成功也會傳回value。
    // 否則就會失敗,并且傳回原有key->value。
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            // 假設 name="&abc" 則轉換後獲得 name="abc"(将其傳回)
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}
      

canonicalName

方法,取指定的 alias 所表示的最終 beanName:

/**
 * Determine the raw name, resolving aliases to canonical names.
 * (翻譯:确定原始名稱,将别名解析為規範名稱。)
 *
 * 根據Bean的别名name,擷取真實的beanName:
 * @param name 根據Bean的别名name
 * @return 傳回真實的beanName
 */
// 位于SimpleAliasRegistry類中的方法:
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    do {
        // aliasName 和 beanName 的映射關系被注冊到 SimpleAliasRegistry 中,
        // do-while循環從該注冊器中根據 aliasName 取到 真實的 beanName:
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}


/** Map from alias to canonical name. */
//  位于SimpleAliasRegistry類中的成員屬性:存儲 映射别名和真實beanName的集合
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
      

3.2 嘗試從緩存中加載Bean的單執行個體

根據上面

transformedBeanName

方法轉換 name 後得到的真實 beanName,

getSingleton(beanName)

方法直接嘗試從緩存中擷取 Bean 的共享單執行個體(單例):

/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    // 根據傳入的name參數,擷取真正的Bean對應的beanName
    String beanName = transformedBeanName(name);
    
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    
    // 根據transformedBeanName方法轉換後的真實beanName,直接嘗試從緩存中擷取Bean的共享單執行個體(單例):
    Object sharedInstance = getSingleton(beanName);
    ...
}
      

在分析getSingleton(beanName)方法之前,我們先來了解下Spring IOC中的三級緩存,以及三級緩存是如何解決循環依賴問題的:

3.2.1 Spring中的三級緩存

所謂三級緩存,其實就是org.springframework.beans.factory包下DefaultSingletonBeanRegistry類中的三個成員屬性:

// Spring一級緩存:用來儲存執行個體化、初始化都完成的對象
// Key:beanName
// Value: Bean執行個體
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// Spring二級緩存:用來儲存執行個體化完成,但是未初始化完成的對象
// Key:beanName
// Value: Bean執行個體
// 和一級緩存一樣也是儲存BeanName和建立bean執行個體之間的關系,
// 與singletonObjects不同之處在于,當一個單例bean被放在裡面後,
// 那麼bean還在建立過程中,就可以通過getBean方法擷取到了,其目的是用來循環檢測引用!(後面會分析)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// Spring三級緩存:用來儲存一個對象工廠,提供一個匿名内部類,用于建立二級緩存中的對象
// Key:beanName
// Value: Bean的工廠
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
      

如圖所示,除了三級緩存是一個HashMap,其他兩個都是ConcurrentHashMap:

Spring源碼學習筆記——Bean加載

Spring之是以引入三級緩存,目的就是為了解決循環依賴問題!

除了上面三個Map集合,還有另一個集合這裡也說一下:

// 用來儲存目前所有已注冊的Bean 
private final Set<String> registeredSingletons = new LinkedHashSet<>(256); 
      

3.2.2 循環依賴問題:

首先,Spring 解決循環依賴有兩個前提條件:

  • Setter方式注入造成的循環依賴(構造器方式注入不可以)
  • 必須是單例

本質上解決循環依賴的問題就是依靠三級緩存,通過三級緩存提前拿到未初始化的對象。下面我們來看一個循環依賴的例子:

Spring源碼學習筆記——Bean加載

A 對象的建立過程:

  1. 建立對象A,執行個體化的時候把A對象工廠放入三級緩存
  2. Spring源碼學習筆記——Bean加載
  3. A 注入屬性時,發現依賴 B,轉而去執行個體化 B
  4. 同樣建立對象 B,注入屬性時發現依賴 A,一次從一級到三級緩存查詢 A,從三級緩存通過對象工廠拿到 A,把 A 放入二級緩存,同時删除三級緩存中的 A,此時,B 已經執行個體化并且初始化完成,把 B 放入一級緩存。
  5. Spring源碼學習筆記——Bean加載
  6. 接着繼續建立 A,順利從一級緩存拿到執行個體化且初始化完成的 B 對象,A 對象建立也完成,删除二級緩存中的 A,同時把 A 放入一級緩存。
  7. 最後,一級緩存中儲存着執行個體化、初始化都完成的A、B 對象。
  8. Spring源碼學習筆記——Bean加載
  9. 從上面5步驟的分析可以看出,三級緩存解決循環依賴是通過把執行個體化和初始化的流程分開了,是以如果都是用構造器的話,就沒法分離這個操作(因為構造器注入執行個體化和初始是一起進行的)。是以構造器方式注入的話是無法解決循環依賴問題的。

解決循環依賴為什麼必須要要三級緩存?二級不行嗎?

答案:不可以!

使用三級緩存而非二級緩存并不是因為隻有三級緩存才能解決循環引用問題,其實二級緩存同樣也能很好解決循環引用問題。

使用三級而非二級緩存并非出于IOC的考慮,而是出于AOP的考慮,即若使用二級緩存,在AOP情形下,往二級緩存中放一個普通的Bean對象,BeanPostProcessor去生成代理對象之後,覆寫掉二級緩存中的普通Bean對象,那麼多線程環境下可能取到的對象就不一緻了。

一句話總結就是,在 AOP 代理增強 Bean 後,會對早期對象造成覆寫,如果多線程情況下可能造成取到的對象不一緻~

了解完什麼是循環依賴和三級緩存後,我們繼續分析getSingleton(beanName)方法,代碼如下:

/**
 * 根據真實beanName到緩存中擷取Bean的共享單執行個體(單例):
 *
 * @param beanName Bean的真實beanName
 * @return
 */
@Override
@Nullable
public Object getSingleton(String beanName) {
     // 第二個參數為true表示允許早期依賴
    return getSingleton(beanName, true);
}

/**
 * 根據真實beanName到緩存中擷取Bean的共享單執行個體(單例):
 *
 * @param beanName Bean的真實beanName
 * @param allowEarlyReference 是否應建立早期參考
 * @return 注冊的單例對象;如果找不到,則傳回null
 */
// 位于DefaultSingletonBeanRegistry類中
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 首先到一級緩存中嘗試擷取beanName對應的單執行個體Bean對象: 
    Object singletonObject = this.singletonObjects.get(beanName);
    
    // 如果條件一成立(singletonObject == null):有幾種可能呢?(2種)
    // 1.單執行個體确實尚未建立...
    // 2.單執行個體正在建立中,目前發生循環依賴了...
    // 條件一成立,條件二也會成立:
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 嘗試從二級緩存中擷取beanName對應的單執行個體Bean對象:
        singletonObject = this.earlySingletonObjects.get(beanName);
        // 條件成立:說明二級緩存沒有找到~
         // 再嘗試從三級緩存中擷取beanName對應的單執行個體Bean對象:
        if (singletonObject == null && allowEarlyReference) {
            // 如果緩存中不存在,鎖住一級緩存singletonObjects
            synchronized (this.singletonObjects) {
                // 這裡再次從一級緩存中去檢索beanName,是為了防止此時有其他線程給一級緩存中添加了該Bean對象(導緻加鎖對象singletonObjects被修改)
                singletonObject = this.singletonObjects.get(beanName);
                // 這裡singletonObject == null 依然成立,說明沒有其他線程修改singletonObjects中的緩存資料,則繼續向下走!
                if (singletonObject == null) {
                    // 同理:從二級緩存中去檢索beanName
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    // 同理:這裡singletonObject == null 依然成立,說明沒有其他線程修改singletonObjects中的緩存資料,則繼續向下走!
                    if (singletonObject == null) {
                        // 從三級緩存中檢索beanName
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        // 如果條件成立:說明3級緩存中有beanName對應的資料,這裡涉及到緩存更新~
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                                // 記錄在緩存中,earlySingletonObjects和singletonFactories互斥!
                            // 向2級緩存中存入該資料
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 将3級緩存的該資料幹掉
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}
      

總結一下上面代碼的執行過程:

首先嘗試從緩存singletonObjects擷取執行個體

如果擷取不到再從earlySingletonObjects擷取

如果還是擷取不到,再從singletonFactories中擷取beanName對應的ObjectFactory

然後調用這個ObjectFactory的getObject方法來建立Bean,放到earlySingletonObjects,并從singletonFactories移除掉這個ObjectFactory

總之這一塊很繁瑣,Spring源碼閱讀就是很燒腦子,真不知設計者是怎樣的天才啊!不感歎了,我們接這這個方法結束後,接這回到doGetBean()方法繼續往下閱讀代碼:

3.3 Bean的執行個體化

getSingleton(beanName)方法執行後,從緩存中得到了 Bean 的原始狀态,接下來需要對該 Bean 進行執行個體化。

緩存中記錄的隻是最原始的 Bean 狀态,并不是我們最終想要的 Bean!

getObjectForBeanInstance方法,其實就是檢測獲得 Bean 是不是 FactoryBean 類型的 Bean。如果是,那麼需要調用該 Bean 對應的 FactoryBean 執行個體中的 getObject() 作為傳回值。

無論是從緩存中擷取到的 Bean 還是通過不同的 scope 政策加載的 Bean 都隻是最原始的 Bean 狀态,并不一定是我們最終想要的 Bean。

假如我們需要對工廠 Bean 進行處理,那麼這裡得到的其實是工廠 Bean 的初始狀态,但我們真正想要的是工廠 Bean 中定義的 factory-method 方法中傳回的 Bean,而getObjectForBeanInstance 方法就是完成這個操作。

/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    // 根據傳入的name參數,擷取真正的Bean對應的beanName
    String beanName = transformedBeanName(name);
    
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    
    // 根據transformedBeanName方法轉換後的真實beanName,直接嘗試從緩存中擷取Bean的共享單執行個體(單例):
    Object sharedInstance = getSingleton(beanName);
    
    // CASE1:
    // 如果緩存中有對應的資料,此時緩存資料可能是普通單執行個體,也可能是 FactoryBean(需要根據name來進行判斷)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 這裡的代碼隻是列印日志,就不再加了
        }
        
        // 擷取sharedInstance對應的Bean執行個體化對象,如果是FactoryBean類型,則可以是該執行個體本身或其建立的子Bean對象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    ...
}
      

getObjectForBeanInstance(sharedInstance, name, beanName, null);中為什麼又要傳入未處理過&的 name 呢?

因為,我們從 IOC 中拿到的對象,它可能是普通單執行個體,也可能是 FactoryBean 執行個體。如果是FactoryBean 執行個體,這個時候還要進行處理。主要是看 name 是否帶&符号。

帶&符号:則說明這次getBean()方法想要拿FactoryBean 對象。

不帶&符号:則說明是要拿FactoryBean 内部管理的執行個體。

3.3.1 從工廠Bean的執行個體中擷取對象

我們跟進getObjectForBeanInstance方法中分析詳細一下:

/**
 * Get the object for the given bean instance, either the bean
 * instance itself or its created object in case of a FactoryBean.
 * (翻譯:擷取給定Bean執行個體的對象,如果是FactoryBean類型,則可以是該執行個體本身或其建立的子Bean對象)
 *
 * 擷取給定Bean執行個體的對象,如果是FactoryBean類型,則可以擷取該執行個體本身或其建立的子Bean對象。
 * @param beanInstance 緩存中拿到的單執行個體對象(初始化狀态,尚未執行個體化)
 * @param name 未處轉換處理過的name(可能帶有&工廠符号)
 * @param beanName 經過轉換處理過的真實beanName
 * @param mbd 合并過後的bd(BeanDefinition)對象
 * @return the object to expose for the bean
 */
protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    // 如果條件成立:說明目前要擷取FactoryBean對象,即name中帶有&符号:
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // 如果beanInstance是null,則直接傳回
        if (beanInstance instanceof NullBean) {
            // 直接傳回
            return beanInstance;
        }
        // 如果條件成立:說明單執行個體對象不是 FactoryBean接口實作類,則驗證不通過,直接抛異常!
         // (指定的name是工廠相關且beanInstance不是FactoryBean類型,則驗證不通過)
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        // 給目前Bean執行個體對應的mbd打個标記,記錄它表達的執行個體是一個FactoryBean.
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        return beanInstance;
    }
    // 執行到這裡,有幾種情況呢?(2種)
    // 情況1.目前Bean執行個體就是普通單執行個體
    // 情況2.目前Bean執行個體是FactoryBean接口實作類,但是本次請求要拿的是FactoryBean實作類内部管理的執行個體。
     // 如果使用者想要直接擷取工廠執行個體而不是工廠的getObject方法對應的執行個體,那麼傳入的name應該加入&
    // 1.目前bean執行個體就是普通單執行個體
    if (!(beanInstance instanceof FactoryBean)) {
        // 直接傳回該單執行個體
        return beanInstance;
    }
    // 2.目前Bean執行個體是FactoryBean接口實作類,但是本次請求要拿的是FactoryBean實作類内部管理的執行個體。
    // 用于儲存FactoryBean執行個體調用getObject方法後得到的傳回值.
    Object object = null;
    if (mbd != null) {
        mbd.isFactoryBean = true;
    }
    else {
        // 嘗試從緩存中加載Bean,如果緩存中已經存在 beanName 對應的工廠 Bean 生成的對象,則直接傳回
        // (即,嘗試到緩存擷取FactoryBean.getObject傳回值)
        object = getCachedObjectForFactoryBean(beanName);
    }
     // 加載FactoryBean
    // 如果條件成立:說明緩存中沒有,就需要到FactoryBean.getObject擷取
    if (object == null) {
        // 到這裡已經明确知道beanInstance一定是FactoryBean類型
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // 判斷Spring中是否有目前beanName對應的BD資訊:
        // containsBeanDefinition檢測beanDefinitionMap中也就是在所有已經加載的類中檢測是否定義beanName
        if (mbd == null && containsBeanDefinition(beanName)) {
            // 将存儲XML配置檔案的GenericBeanDefinition轉換為RootBeanDefinition,
            // 如果指定BeanName是子Bean的話同時會合并父類的相關屬性
            // 為什麼是合并後的呢?因為咱們的BD是直接繼承的。合并後的BD資訊是包含繼承回來的BD。
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // 是否是使用者定義的而不是應用程式本身定義的:
        // synthetic 預設值是false 表示這是一個使用者對象,如果是true 表示是系統對象。
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        /**
         * getObjectFromFactoryBean是本方法的核心,從給定的FactoryBean中擷取一個Bean對象:
         *
         * factory:FactoryBean執行個體
         * beanName:FactoryBean執行個體中的Bean對應的beanName
         * synthetic:如果是使用者對象,這個值就是true,大部分情況下都是true。
         */
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
      

總的來說,getObjectForBeanInstance 中做了如下幾件事情:

對 beanInstance 進行 FactoryBean 正确性的校驗;

對非 FactoryBean 類型或者本身就需要傳回 FactoryBean 類型對象的 beanInstance 不作處理;

如果緩存中已經存在 beanName 對應的工廠 Bean 生成的對象則直接傳回;

如果緩存沒有,則對 beanInstance 進行強轉為 FactoryBean 類型,将從 Factory 中解析擷取 Bean 的工作委托給 getObjectFromFactoryBean(factory, beanName, !synthetic) 方法。

下面我們繼續跟進getObjectFromFactoryBean(factory, beanName, !synthetic) 方法:

3.3.2 從給定的FactoryBean中擷取一個Bean對象的核心方法

/**
 * 從給定的FactoryBean中擷取一個Bean對象:
 * @param factory FactoryBean執行個體
 * @param beanName FactoryBean執行個體中的Bean對應的beanName
 * @param shouldPostProcess 如果是使用者對象,這個值就是true,大部分情況下都是true。
 * @return 傳回從FactoryBean獲得的Bean對象
 * @throws BeanCreationException if FactoryBean object creation failed
 * @see org.springframework.beans.factory.FactoryBean#getObject()
 */
// 位于FactoryBeanRegistrySupport中的方法:
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 如果條件成立:說明factory是單例的同時,factory還得已經被執行個體化
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // 再次嘗試從緩存中擷取bean
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 委托doGetObjectFromFactoryBean方法産生Bean
                object = doGetObjectFromFactoryBean(factory, beanName);
                // 再次判斷緩存中是存在beanName對應的Bean
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            // 調用ObjectFactory的後處理器
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    // 單例模式下:已經加載的bean要記錄下來,便于下次複用
                    if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}
      

如果傳回的 Bean 是單例的,就必須保證全局唯一,同時不可重複建立,可以使用緩存來提高性能,也就是說加載過就記錄下來以便于下次複用。如果傳回的 Bean 不是單例的,則直接建立 Bean。

Spring是怎麼進行保證單例模式下 Bean 的全局唯一呢?在代碼 16 和 19 行,分别在建立 Bean 前後進行雙重判斷緩存中是否能通過 beanName 擷取到執行個體。

這裡的緩存就是 this.factoryBeanObjectCache 這個 ConcurrentHashMap 。該方法在建立完成 Bean 後,最後會将其添加至緩存。

我們大緻講了 getObjectFromFactoryBean() 方法的前面和末尾的流程,其中建立 Bean 的工作實際交給了 doGetObjectFromFactoryBean(factory, beanName) 方法去完成。

3.3.3 真正從FactoryBean中擷取一個Bean執行個體的方法

又是doXxx方法,我們前面說過,Spring架構中經常出現Xxx方法和doXxx方法,内外層配套使用,真正執行代碼邏輯的是内層doXxx方法。

/**
 * 真正從FactoryBean中擷取一個Bean執行個體的方法
 * @param factory FactoryBean執行個體
 * @param beanName FactoryBean執行個體中的Bean對應的beanName
 * @return 傳回從FactoryBean獲得的Bean對象
 * @throws BeanCreationException if FactoryBean object creation failed
 * @see org.springframework.beans.factory.FactoryBean#getObject()
 */
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    // 作為要傳回的結果
    Object object;
    try {
         // 需要權限認證(略)
        if (System.getSecurityManager() != null) {
            ...
        }
        else {
              //  factory.getObject()方法擷取bean   
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {// 略}
    catch (Throwable ex) {// 略}
        
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            ...
        }
        object = new NullBean();
    }
    return object;
}
      

從上面的代碼中,我們可以清晰的看到調用了 FactoryBean 的 getObject() 方法獲得 Bean執行個體。

我們再回到 getObjectFromFactoryBean() 方法,通過 doGetObjectFromFactoryBean(factory, beanName) 方法建立出 Bean 并沒有直接傳回,首先再次判斷緩存中是否有,如果有那麼用緩存的 Bean 傳回,沒問題。

如果确認緩存沒有,那麼理論上應該直接傳回,但是并沒有。Spring還調用 ObjectFactory 的後處理器對 Bean 進行處理。(即,子類 AbstractAutowireCapableBeanFactory 的 postProcessObjectFromFactoryBean(object, beanName) 方法)

下面回到doGetBean()方法繼續往下跟進代碼邏輯:

/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    // 1.根據傳入的name參數,擷取真正的Bean對應的beanName
    String beanName = transformedBeanName(name);
    
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    
    // 2.根據transformedBeanName方法轉換後的真實beanName,直接嘗試從緩存中擷取Bean的共享單執行個體(單例):
    Object sharedInstance = getSingleton(beanName);
    
    // CASE1:
    // 如果緩存中有對應的資料,此時緩存資料可能是普通單執行個體,也可能是 FactoryBean(需要根據name來進行判斷)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 這裡的代碼隻是列印日志,就不再加了
        }
        
        // 3.Bean的執行個體化
        // 擷取sharedInstance對應的Bean執行個體化對象,如果是FactoryBean類型,則可以是該執行個體本身或其建立的子Bean對象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根據beanName從緩存中沒有找到對應的資料 (那麼我們就需要自己建立了...)
    else {
        // 4.原型模式(prototype)的依賴檢查: Spring隻能給解決單例模式下的循環依賴問題,
        // 如果原型模式(prototype)出現循環依賴,則直接抛異常~
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        
        // 5.檢測parentBeanFactory
        // 如果beanDefinitionMap中也就是已經加載的類中不包括beanName,則嘗試從parentBeanFactory中檢測
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
           ...
        }
        ...
    }                                             
}
      

3.4 原型模式(prototype)的依賴檢查

在3.2.2小節,我們分析了什麼循環依賴,以及Spring如何解決循環依賴問題。從中得出的結論是:

Spring解決循環依賴必須滿足2個條件:

是以上面代碼中原型模式(prototype)的依賴檢查,目的就是檢測原型模式下是否出現循環依賴問題,如果出現,則Spring直接抛出異常throw new BeanCurrentlyInCreationException(beanName);

// 3.原型模式(prototype)的依賴檢查: Spring隻能給解決單例模式下的循環依賴問題,
// 如果原型模式(prototype)出現循環依賴,則直接抛異常~
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}
      

3.5 檢測parentBeanFactory

如果第2步

getSingleton(beanName);

從緩存中沒有檢索到 beanName 對應的結果,那麼就轉向父工廠去檢索。

getParentBeanFactory();

方法就是擷取父工廠:

@Override
@Nullable
public BeanFactory getParentBeanFactory() {
    return this.parentBeanFactory;
}

/** Parent bean factory, for bean inheritance support. */
// 父Bean工廠,用于支援Bean的繼承
@Nullable
private BeanFactory parentBeanFactory;
      

containsBeanDefinition(beanName)

方法的作用是擷取目前 BeanDefinitionMap (XML配置檔案)中的 beanName 對應的配置。如果擷取到則傳回 

true

,否則傳回

false

// 如果條件成立,說明:父工廠不為空,且目前BeanDefinitionMap中無法擷取beanName對應的Bean對象
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // 那麼就隻能到父工程parentBeanFactory中嘗試去擷取Bean了
    // parentBeanFactory.doGetBean() 相當于遞歸調用~
    ...
}
      

完整代碼如下:

// 如果beanDefinitionMap中也就是已經加載的類中不包括beanName則嘗試從parentBeanFactory中檢測
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    String nameToLookup = originalBeanName(name);
    if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                nameToLookup, requiredType, args, typeCheckOnly);
    }
    else if (args != null) {
        // Delegation to parent with explicit args.
        // 遞歸到BeanFactory中尋找
        return (T) parentBeanFactory.getBean(nameToLookup, args);
    }
    else if (requiredType != null) {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
    else {
        return (T) parentBeanFactory.getBean(nameToLookup);
    }
}
      

下面繼續回到

doGetBean()

方法往下跟進:

/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    // 1.根據傳入的name參數,擷取真正的Bean對應的beanName
    String beanName = transformedBeanName(name);
    
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    
    // 2.根據transformedBeanName方法轉換後的真實beanName,直接嘗試從緩存中擷取Bean的共享單執行個體(單例):
    Object sharedInstance = getSingleton(beanName);
    
    // CASE1:
    // 如果緩存中有對應的資料,此時緩存資料可能是普通單執行個體,也可能是 FactoryBean(需要根據name來進行判斷)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 這裡的代碼隻是列印日志,就不再加了
        }
        
        // 3.Bean的執行個體化
        // 擷取sharedInstance對應的Bean執行個體化對象,如果是FactoryBean類型,則可以是該執行個體本身或其建立的子Bean對象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根據beanName從緩存中沒有找到對應的資料 (那麼我們就需要自己建立了...)
    else {
        // 4.原型模式(prototype)的依賴檢查: Spring隻能給解決單例模式下的循環依賴問題,
        // 如果原型模式(prototype)出現循環依賴,則直接抛異常~
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        
        // 5.檢測parentBeanFactory
        // 如果beanDefinitionMap中也就是已經加載的類中不包括beanName,則嘗試從parentBeanFactory中檢測
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
           ...
        }
        
        // 如果從緩存中擷取不到, 同時目前的 beanDefinitionMap 中存在對應 beanName 的配置,
        // 我們就可以依據包含有XML配置檔案資訊的 beanDefinition 進行建立 bean 了。
        try {
            ...
                
            // 6.GenericBeanDefinition轉為RootBeanDefinition:
            // 将存儲XML配置檔案的GenericBeanDefinition轉換為RootBeanDefinition,
            // 如果指定BeanName是子Bean的話同時會合并父類的相關屬性    
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 判斷目前BD是否為抽象BD,抽象BD不能建立執行個體,隻能作為父BD讓子BD去繼承。
            checkMergedBeanDefinition(mbd, beanName, args);
            
            // 7.尋找依賴
            String[] dependsOn = mbd.getDependsOn();
            // 若存在依賴則需要遞歸執行個體化依賴的bean
            if (dependsOn != null) {
                ...
            }
            
            ...
        catch (BeansException ex) {
            ...
        }
        finally {
            beanCreation.end();
        }
    }                                             
}
      

3.6 GenericBeanDefinition轉為RootBeanDefinition

如果從緩存中擷取不到,且目前的 beanDefinitionMap 中存在對應 beanName 的配置,我們就可以依據包含有XML配置檔案資訊的 beanDefinition 進行建立 Bean !

從XML配置檔案中讀取到的 Bean 資訊是利用 GenericBeanDefinition 存儲的,但是後面Spring對所有 Bean 的後續處理都是針對于 RootBeanDefinition 的,是以需要進行轉換,轉換的同時如果父類 Bean 不為空,則會一并合并父類的屬性。

// 将存儲XML配置檔案的GenericBeanDefinition轉換為RootBeanDefinition,
// 如果指定BeanName是子Bean的話同時會合并父類的相關屬性
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 判斷目前BD是否為抽象BD,抽象BD不能建立執行個體,隻能作為父BD讓子BD去繼承。
checkMergedBeanDefinition(mbd, beanName, args);
      

3.7 尋找依賴(依賴注入)

因為 Bean 的初始化過程中很可能會用到某些屬性,而某些屬性很可能是動态配置的,并且配置的成依賴于其他的 Bean,那麼此時應該先加載依賴的 Bean。是以在流程中,Spring初始化一個 Bean,會先初始化其依賴的所有的其他 Bean。

// 7.尋找依賴
// eg: <bean name="A" depends-on="B" ... />
String[] dependsOn = mbd.getDependsOn();
// 若存在依賴則需要遞歸執行個體化依賴的Bean
if (dependsOn != null) {
   for (String dep : dependsOn) {
      // 判斷循環依賴:\
      // <bean name="A" depends-on="B" ... />
     // <bean name="B" depends-on="A" .../>
      // Spring是處理不了這種情況的,需要抛異常...
      if (isDependent(beanName, dep)) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
      }
      // 緩存依賴調用
      registerDependentBean(dep, beanName);
      try {
         getBean(dep);
      }
      catch (NoSuchBeanDefinitionException ex) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
      }
   }
}
      

3.8 根據不同的scope作用域建立Bean

Spring中包含如下幾個作用域:

singleton 作用域

prototype 作用域

request 作用域

session 作用域

global session 作用域

我們重點分析單例singleton 作用域建立Bean的邏輯,其他幾種情況與其類似,不再重複分析:

// 8.根據不同的scope作用域建立Bean
// CASE-SINGLETON:建立單執行個體Bean的邏輯(singleton模式的建立)
if (mbd.isSingleton()) {
    // 第二個getSingleton方法,擷取單例Bean執行個體,這個方法更傾向于建立執行個體并傳回:
    sharedInstance = getSingleton(beanName, () -> {
        try {
            // 建立單例Bean的核心方法
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put ther
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    });
    //這 裡為啥不直接傳回,還調用getObjectForBeanInstance(...)?
    // 這裡為什麼又要套呢?為啥不直接拿回去用呢?
    // 其實,你從IOC中拿到的對象,它可能是普通單執行個體,也可能是FactoryBean執行個體。
    // 如果是FactoryBean執行個體,這個時候還要進行處理。主要是看name是帶“&” 還是 不帶“&”,
    // 帶“&”說明這次getBean想要拿FactoryBean對象。
    // 否則是要拿FactoryBean内部管理的執行個體。
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// CASE-PROTOTYPE: 建立多執行個體Bean的邏輯(prototype模式的建立)
else if (mbd.isPrototype()) {
   // prototype原型模式的建立(new)
   .....
}
// CASE-OTHER:其他scope作用域建立Bean的邏輯,例如request、session、global session
else {
   // 指定的scope上執行個體化bean
   .....
}
      

上面代碼中,擷取單例 Bean 的 getSingleton(String beanName, ObjectFactory singletonFactory)方法是從緩存種擷取Bean執行個體的getSingleton(String beanName, boolean allowEarlyReference)方法的重載方法。我們跟進該方法檢視:

/**
 * 緩存中沒有需要新建立單例初始狀态的bean
 * @param beanName Bean對象對應的beanName
 * @param singletonFactory Bean工廠
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   // 全局變量需要同步
   synchronized (this.singletonObjects) {
      // 首先檢查對應的bean是否已經加載過,因為是單例模式的,如果有直接傳回
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         // 單例bean的初始化
          
         if (this.singletonsCurrentlyInDestruction) {
            ... // 抛異常
         }
         if (logger.isDebugEnabled()) {
            ... // 日志列印
         }

         // 記錄加載狀态,通過this.singletonsCurrentlyInCreation.add(beanName)将目前正要建立的bean記錄在緩存中,
         // 便于對循環依賴進行檢測
         beforeSingletonCreation(beanName);

         // 建立單例bean是否成功的辨別
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            // 初始化Bean
            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) {
            ... // 捕獲異常
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }

            // 建立完Bean之後需要移除緩存中對該Bean正在加載狀态的記錄
            afterSingletonCreation(beanName);
         }
         // 加入緩存并删除加載Bean過程中所記錄的各種輔助狀态
         if (newSingleton) {
            addSingleton(beanName, singletonObject);
         }
      }
      
      // 傳回處理結果
      return singletonObject;
   }
}
      

我們跟進看一下

addSingleton(beanName, singletonObject);

方法:

/**
 * 添加緩存并移除輔助變量
 * @param beanName the name of the bean
 * @param singletonObject the singleton object
 */
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 加入緩存
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}
      

分析完上面代碼,接下來去研究一下createBean(beanName, mbd, args) 方法:

3.8.1 準備建立Bean的方法

按照Spring的方法命名風格,真正建立Bena的應該是doCreateBaen()方法,是以該部分主要研究Spring的createBean() 方法中在 doCreateBean() 之前先進行了哪些準備工作。

/**
 * 建立單例Bean的核心方法:(其實是準備建立Bean之前需要做的一些工作)
 * @see #doCreateBean
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   // mbdToUse: 建立執行個體時使用mbd
   // 在XML配置檔案中用 parent 屬性可以定義父 <bean> 和子 <bean> ,父 <bean> 用 RootBeanDefinition表示
   RootBeanDefinition mbdToUse = mbd;

   // 鎖定class,根據設定的class屬性或者根據className來解析Class
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    
   // 當解析出class同時mbd中beanClass屬性設定為字元串對象而不是Class對象時,需要對mbd進行更新為Class類型:
   // 條件一 resolvedClass != null:說明拿到了mbd執行個體化對象時的真實Class對象。
   // 條件二 !mbd.hasBeanClass():條件成立,說明mbd在resolveBeanClass之前,是沒有Class對象的。
   // 條件三 bd.getBeanClassName()!= null:成立,說明mbd有ClassName 
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // 驗證override及準備覆寫的方法(預處理...直接跳過,不作為重點)
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      ... // 異常抛出
   }

   try {
      // 對BeanDefinition中的屬性做些前置處理,給BeanPostProcessors一個機會來傳回代理來替代真正的執行個體
      // 注意,這裡的代理對象不是Spring AOP邏輯實作的地方。!
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      // 如果前置處理傳回的不為空,則直接傳回(AOP功能就是這裡判斷的),否則需要進行正常bean的建立
      // bean != null:條件成立會形成一個短路操作,這裡直接傳回了.
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      ... // 異常抛出
   }

   try {
      // 核心方法(真正建立Bean的方法):建立Bean執行個體對象,并且生命周期的動作大部分都在這裡 
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isDebugEnabled()) {
         ... // 日志列印
      }
      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      ... // 異常處理
   }
   catch (Throwable ex) {
      ... // 異常處理
   }
}
      

代碼的處理流程大緻如下:

設定 class 屬性或者根據 className 來解析 class ;

對 MethodOverride 屬性進行驗證及标記;

應用初始化前的後處理器,解析指定 bean 是否存在應用初始化前的短路操作;

建立 bean。

3.8.2 真正建立Bean的方法

doCreateBean 方法,就是真正去建立Bean的方法,這一部分代碼量很多,且非常複雜,是以單獨抽出來一篇文章進行補充:Spring源碼解析——Bean加載(doCreateBean方法補充)

3.9 類型轉換

根據 scope 建立完 Bean 成功後,一般可以直接傳回即可。但當傳入 doGetBean 方法中的 requireType 參數不為空時,意味着我們對最後傳回的 Bean 有着類型上的要求。Spring一般通過類型轉換器将第⑧步建立完成的 Bean 轉換為 requireType 指定的類型。Spring自身提供了一些轉換器,使用者也可以自己擴充轉換器來滿足需求。

我們先回到doGetBean方法再來你全局看一下:

/**
 * 真正去擷取Bean對象的方法:
 * @param name Bean對象的名稱 -> 根據name參數擷取對應的Bean對象
 * @param requiredType 檢索所需的Bean類型
 * @param args 使用顯式參數建立bean執行個體時要使用的參數(僅在建立新執行個體而不是檢索現有執行個體時才應用)
 * @param typeCheckOnly 是否擷取執行個體用于類型檢查而不是實際使用
 * @return 傳回Bean的一個執行個體
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    
    // 1.根據傳入的name參數,擷取真正的Bean對應的beanName
    String beanName = transformedBeanName(name);
    
    // 用于保留傳回值(要傳回的Bean執行個體對象)
    Object beanInstance;
    
    // 2.根據transformedBeanName方法轉換後的真實beanName,直接嘗試從緩存中擷取Bean的共享單執行個體(單例):
    Object sharedInstance = getSingleton(beanName);
    
    // CASE1:
    // 如果緩存中有對應的資料,此時緩存資料可能是普通單執行個體,也可能是 FactoryBean(需要根據name來進行判斷)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 這裡的代碼隻是列印日志,就不再加了
        }
        
        // 3.Bean的執行個體化
        // 擷取sharedInstance對應的Bean執行個體化對象,如果是FactoryBean類型,則可以是該執行個體本身或其建立的子Bean對象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根據beanName從緩存中沒有找到對應的資料 (那麼我們就需要自己建立了...)
    else {
        // 4.原型模式(prototype)的依賴檢查: Spring隻能給解決單例模式下的循環依賴問題,
        // 如果原型模式(prototype)出現循環依賴,則直接抛異常~
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        
        // 5.檢測parentBeanFactory
        // 如果beanDefinitionMap中也就是已經加載的類中不包括beanName,則嘗試從parentBeanFactory中檢測
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
           ...
        }
        
        // 如果從緩存中擷取不到, 同時目前的 beanDefinitionMap 中存在對應 beanName 的配置,
        // 我們就可以依據包含有XML配置檔案資訊的 beanDefinition 進行建立 bean 了。
        try {
            ...
                
            // 6.GenericBeanDefinition轉為RootBeanDefinition:
            // 将存儲XML配置檔案的GenericBeanDefinition轉換為RootBeanDefinition,
            // 如果指定BeanName是子Bean的話同時會合并父類的相關屬性    
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 判斷目前BD是否為抽象BD,抽象BD不能建立執行個體,隻能作為父BD讓子BD去繼承。
            checkMergedBeanDefinition(mbd, beanName, args);
            
            // 7.尋找依賴
            String[] dependsOn = mbd.getDependsOn();
            // 若存在依賴則需要遞歸執行個體化依賴的bean
            if (dependsOn != null) {
                ...
            }
            
            // 8.根據不同的scope作用域建立Bean
            // CASE-SINGLETON:建立單執行個體Bean的邏輯(singleton模式的建立)
            if (mbd.isSingleton()) {
                ...
            }
            // CASE-PROTOTYPE: 建立多執行個體Bean的邏輯(prototype模式的建立)
            else if (mbd.isPrototype()) {
               // prototype原型模式的建立(new)
               .....
            }
            // CASE-OTHER:其他scope作用域建立Bean的邏輯,例如request、session、global session
            else {
               // 指定的scope上執行個體化bean
               .....
            }
        catch (BeansException ex) {
            ...
        }
        finally {
            beanCreation.end();
        }
            
        // 9.對最終得到的Bean執行個體進行類型轉換
        return adaptBeanInstance(name, beanInstance, requiredType);
    }                                             
}
      

adaptBeanInstance(name, beanInstance, requiredType);

@SuppressWarnings("unchecked")
<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
    // 檢查需要的類型是否符合Bean的實際類型
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
             // 類型轉換:
            Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return (T) 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());
        }
    }
    return (T) bean;
}
      

經過以上的九個步驟,Bean 就算建立完成了(即,完整的doGetBean方法的邏輯執行完畢)。其中第8步最為關鍵——針對不同的 scope 進行 Bean 的建立。

3.10 小節

我們來大緻總結一下一個 Bean 的建立過程(即,一個doGetBean方法的邏輯):

① 根據傳入的 name 參數,經過轉換擷取真實的 beanName

② 根據真實的 beanName 向緩存中擷取Bean的單執行個體(這時候擷取的是初始狀态,尚未執行個體化)

③ 對 Bean 的單執行個體進行執行個體化

④ 對 Bean 的單執行個體進行依賴注入

⑤ 對 Bean 的單執行個體進行初始化

⑥ 對最終得到的對 Bean 的單執行個體進行類型轉換

成功加載 Bean 對象!

4.參考文章

https://blog.csdn.net/firefile/article/details/90735264 https://blog.csdn.net/sinat_35663368/article/details/102524884 https://mp.weixin.qq.com/s/AHl52PZOU-jlRLIVps2_qw https://wangguoping.blog.csdn.net/article/details/82859993

如果文章對大家有幫助,請三連支援一下!

Spring源碼學習筆記——Bean加載