spring原始碼深度解析— IOC 之 開啟 bean 的載入

chen_hao發表於2019-07-05

概述

前面我們已經分析了spring對於xml配置檔案的解析,將分析的資訊組裝成 BeanDefinition,並將其儲存註冊到相應的 BeanDefinitionRegistry 中。至此,Spring IOC 的初始化工作完成。接下來我們將對bean的載入進行探索。

之前系列文章:

spring原始碼深度解析— IOC 之 容器的基本實現

spring原始碼深度解析— IOC 之 預設標籤解析(上)

spring原始碼深度解析— IOC 之 預設標籤解析(下)

spring原始碼深度解析— IOC 之 自定義標籤解析

BeanFactory

當我們顯示或者隱式地呼叫 getBean() 時,則會觸發載入 bean 階段。如下:

public class AppTest {
    @Test
    public void MyTestBeanTest() {
        BeanFactory bf = new XmlBeanFactory( new ClassPathResource("spring-config.xml"));
        MyTestBean myTestBean = (MyTestBean) bf.getBean("myTestBean");
    }
}

我們看到這個方法是在介面BeanFactory中定義的,我們看下BeanFactory體系結構,如下圖所示: 

從上圖我們看到:  
(1)BeanFactory作為一個主介面不繼承任何介面,暫且稱為一級介面。
(2)有3個子介面繼承了它,進行功能上的增強。這3個子介面稱為二級介面。
(3)ConfigurableBeanFactory可以被稱為三級介面,對二級介面HierarchicalBeanFactory進行了再次增強,它還繼承了另一個外來的介面SingletonBeanRegistry
(4)ConfigurableListableBeanFactory是一個更強大的介面,繼承了上述的所有介面,無所不包,稱為四級介面。(這4級介面是BeanFactory的基本介面體系。繼續,下面是繼承關係的2個抽象類和2個實現類:)
(5)AbstractBeanFactory作為一個抽象類,實現了三級介面ConfigurableBeanFactory大部分功能。
(6)AbstractAutowireCapableBeanFactory同樣是抽象類,繼承自AbstractBeanFactory,並額外實現了二級介面AutowireCapableBeanFactory
(7)DefaultListableBeanFactory繼承自AbstractAutowireCapableBeanFactory,實現了最強大的四級介面ConfigurableListableBeanFactory,並實現了一個外來介面BeanDefinitionRegistry,它並非抽象類。
(8)最後是最強大的XmlBeanFactory,繼承自DefaultListableBeanFactory,重寫了一些功能,使自己更強大。 

定義

  BeanFactory,以Factory結尾,表示它是一個工廠類(介面), 它負責生產和管理bean的一個工廠。在Spring中,BeanFactory是IOC容器的核心介面,它的職責包括:例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。BeanFactory只是個介面,並不是IOC容器的具體實現,但是Spring容器給出了很多種實現,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一個,該實現將以XML方式描述組成應用的物件及物件間的依賴關係。XmlBeanFactory類將持有此XML配置後設資料,並用它來構建一個完全可配置的系統或應用。   

  BeanFactory是Spring IOC容器的鼻祖,是IOC容器的基礎介面,所有的容器都是從它這裡繼承實現而來。可見其地位。BeanFactory提供了最基本的IOC容器的功能,即所有的容器至少需要實現的標準。

  XmlBeanFactory,只是提供了最基本的IOC容器的功能。而且XMLBeanFactory,繼承自DefaultListableBeanFactory。DefaultListableBeanFactory實際包含了基本IOC容器所具有的所有重要功能,是一個完整的IOC容器。

  ApplicationContext包含BeanFactory的所有功能,通常建議比BeanFactory優先。
  BeanFactory體系結構是典型的工廠方法模式,即什麼樣的工廠生產什麼樣的產品。BeanFactory是最基本的抽象工廠,而其他的IOC容器只不過是具體的工廠,對應著各自的Bean定義方法。但同時,其他容器也針對具體場景不同,進行了擴充,提供具體的服務。 如下:

Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"});
BeanFactory factory = (BeanFactory) context;

基本就是這些了,接著使用getBean(String beanName)方法就可以取得bean的例項;BeanFactory提供的方法及其簡單,僅提供了六種方法供客戶呼叫:

  • boolean containsBean(String beanName) 判斷工廠中是否包含給定名稱的bean定義,若有則返回true
  • Object getBean(String) 返回給定名稱註冊的bean例項。根據bean的配置情況,如果是singleton模式將返回一個共享例項,否則將返回一個新建的例項,如果沒有找到指定bean,該方法可能會丟擲異常
  • Object getBean(String, Class) 返回以給定名稱註冊的bean例項,並轉換為給定class型別
  • Class getType(String name) 返回給定名稱的bean的Class,如果沒有找到指定的bean例項,則排除NoSuchBeanDefinitionException異常
  • boolean isSingleton(String) 判斷給定名稱的bean定義是否為單例模式
  • String[] getAliases(String name) 返回給定bean名稱的所有別名 
package org.springframework.beans.factory;  
import org.springframework.beans.BeansException;  
public interface BeanFactory {  
    String FACTORY_BEAN_PREFIX = "&";  
    Object getBean(String name) throws BeansException;  
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;  
    <T> T getBean(Class<T> requiredType) throws BeansException;  
    Object getBean(String name, Object... args) throws BeansException;  
    boolean containsBean(String name);  
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;  
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;  
    boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;  
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;  
    String[] getAliases(String name);  
}

FactoryBean

一般情況下,Spring通過反射機制利用<bean>的class屬性指定實現類例項化Bean,在某些情況下,例項化Bean過程比較複雜,如果按照傳統的方式,則需要在<bean>中提供大量的配置資訊。配置方式的靈活性是受限的,這時採用編碼的方式可能會得到一個簡單的方案。Spring為此提供了一個org.springframework.bean.factory.FactoryBean的工廠類介面,使用者可以通過實現該介面定製例項化Bean的邏輯。FactoryBean介面對於Spring框架來說佔用重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了例項化一些複雜Bean的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支援泛型,即介面宣告改為FactoryBean<T>的形式。

以Bean結尾,表示它是一個Bean,不同於普通Bean的是:它是實現了FactoryBean<T>介面的Bean,根據該Bean的ID從BeanFactory中獲取的實際上是FactoryBean的getObject()返回的物件,而不是FactoryBean本身,如果要獲取FactoryBean物件,請在id前面加一個&符號來獲取。後面我們會從原始碼來分析這一塊

package org.springframework.beans.factory;  
public interface FactoryBean<T> {  
    T getObject() throws Exception;  
    Class<?> getObjectType();  
    boolean isSingleton();  
}

在該介面中還定義了以下3個方法:

  • T getObject():返回由FactoryBean建立的Bean例項,如果isSingleton()返回true,則該例項會放到Spring容器中單例項快取池中;
  • boolean isSingleton():返回由FactoryBean建立的Bean例項的作用域是singleton還是prototype;
  • Class<T> getObjectType():返回FactoryBean建立的Bean型別。

當配置檔案中<bean>的class屬性配置的實現類是FactoryBean時,通過getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的物件,相當於FactoryBean#getObject()代理了getBean()方法。
例:如果使用傳統方式配置下面Car的<bean>時,Car的每個屬性分別對應一個<property>元素標籤。

public class Car {  
    private int maxSpeed ;  
    private String brand ;  
    private double price ;  
    //get//set 方法
}

如果用FactoryBean的方式實現就靈活點,下例通過逗號分割符的方式一次性的為Car的所有屬性指定配置值:

import  org.springframework.beans.factory.FactoryBean;  
public  class CarFactoryBean implements  FactoryBean<Car> {  
    private String carInfo ;  
    public  Car getObject()  throws  Exception  {  
        Car car = new  Car();  
        String[] infos = carInfo.split(","); 
        car.setBrand(infos[0]);
        car.setMaxSpeed(Integer.valueOf(infos[1]));
        car.setPrice(Double.valueOf(infos[2]));
        return  car;  
    }  
    public  Class<Car> getObjectType(){  
        return Car.class ;  
    }  
    public boolean isSingleton(){  
        return false ;  
    }  
    public String getCarInfo(){  
        return  this.carInfo;  
    }  
  
    //接受逗號分割符設定屬性資訊  
    public void setCarInfo (String carInfo){  
        this.carInfo = carInfo;  
    }  
}

有了這個CarFactoryBean後,就可以在配置檔案中使用下面這種自定義的配置方式配置CarBean了:

<bean d="car"class="com.chenhao.spring.CarFactoryBean" P:carInfo="大奔,600,1000000"/>

當呼叫getBean("car")時,Spring通過反射機制發現CarFactoryBean實現了FactoryBean的介面,這時Spring容器就呼叫介面方法CarFactoryBean#getObject()方法返回。如果希望獲取CarFactoryBean的例項,則需要在使用getBean(beanName)方法時在beanName前顯示的加上"&"字首:如getBean("&car");  

獲取bean

接下來我們回到載入bean的階段,當我們顯示或者隱式地呼叫 getBean() 時,則會觸發載入 bean 階段。如下:

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

內部呼叫 doGetBean() 方法,這個方法的程式碼比較長,各位耐心看下:

@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
        @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    //獲取 beanName,這裡是一個轉換動作,將 name 轉換為 beanName
    final String beanName = transformedBeanName(name);
    Object bean;
    /*
     *檢查快取中的例項工程是否存在對應的例項
     *為何要優先使用這段程式碼呢?
     *因為在建立單例bean的時候會存在依賴注入的情況,而在建立依賴的時候為了避免迴圈依賴
     *spring建立bean的原則是在不等bean建立完就會將建立bean的objectFactory提前曝光,即將其加入到快取中,一旦下個bean建立時依賴上個bean則直接使用objectFactory          
     *直接從快取中或singletonFactories中獲取objectFactory
     *就算沒有迴圈依賴,只是單純的依賴注入,如B依賴A,如果A已經初始化完成,B進行初始化時,需要遞迴呼叫getBean獲取A,這是A已經在快取裡了,直接可以從這裡取到
     */
    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        //返回對應的例項,有些時候並不是直接返回例項,而是返回某些方法返回的例項
        //這裡涉及到我們上面講的FactoryBean,如果此Bean是FactoryBean的實現類,如果name字首為"&",則直接返回此實現類的bean,如果沒有字首"&",則需要呼叫此實現類的getObject方法,返回getObject裡面真是的返回物件
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else {
        //只有在單例的情況下才會解決迴圈依賴
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        //嘗試從parentBeanFactory中查詢bean
        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.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }
        //如果不是僅僅做型別檢查,則這裡需要建立bean,並做記錄
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }
        try {
            //將儲存XML配置檔案的GenericBeanDefinition轉換為RootBeanDefinition,同時如果存在父bean的話則合併父bean的相關屬性
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            //如果存在依賴則需要遞迴例項化依賴的bean
            String[] dependsOn = mbd.getDependsOn();
            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 + "'");
                    }
                    registerDependentBean(dep, beanName);
                    try {
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // 單例模式
            // 例項化依賴的bean後對bean本身進行例項化
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        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;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // 原型模式
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            // 從指定的 scope 下建立 bean
            else {
                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 {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    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.
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

程式碼是相當長,處理邏輯也是相當複雜,下面將其進行拆分講解。

獲取 beanName

final String beanName = transformedBeanName(name);

這裡傳遞的是 name,不一定就是 beanName,可能是 aliasName,也有可能是 FactoryBean(帶“&”字首),所以這裡需要呼叫 transformedBeanName() 方法對 name 進行一番轉換,主要如下:

protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

// 去除 FactoryBean 的修飾符
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    String beanName = name;
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}

// 轉換 aliasName
public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

主要處理過程包括兩步:

  1. 去除 FactoryBean 的修飾符。如果 name 以 “&” 為字首,那麼會去掉該 “&”,例如,name = "&studentService",則會是 name = "studentService"
  2. 取指定的 alias 所表示的最終 beanName。主要是一個迴圈獲取 beanName 的過程,例如別名 A 指向名稱為 B 的 bean 則返回 B,若 別名 A 指向別名 B,別名 B 指向名稱為 C 的 bean,則返回 C。

快取中獲取單例bean

單例在Spring的同一個容器內只會被建立一次,後續再獲取bean直接從單例快取中獲取,當然這裡也只是嘗試載入,首先嚐試從快取中載入,然後再次嘗試從singletonFactorry載入因為在建立單例bean的時候會存在依賴注入的情況,而在建立依賴的時候為了避免迴圈依賴,Spring建立bean的原則不等bean建立完成就會建立bean的ObjectFactory提早曝光加入到快取中,一旦下一個bean建立時需要依賴上個bean,則直接使用ObjectFactory;就算沒有迴圈依賴,只是單純的依賴注入,如B依賴A,如果A已經初始化完成,B進行初始化時,需要遞迴呼叫getBean獲取A,這是A已經在快取裡了,直接可以從這裡取到。接下來我們看下獲取單例bean的方法getSingleton(beanName),進入方法體:

@Override
@Nullable
public Object getSingleton(String beanName) {
    //引數true是允許早期依賴
    return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //檢查快取中是否存在例項,這裡就是上面說的單純的依賴注入,如B依賴A,如果A已經初始化完成,B進行初始化時,需要遞迴呼叫getBean獲取A,這是A已經在快取裡了,直接可以從這裡取到
    Object singletonObject = this.singletonObjects.get(beanName);
    //如果快取為空且單例bean正在建立中,則鎖定全域性變數,為什麼要判斷bean在建立中呢?這裡就是可以判斷是否迴圈依賴了。
    //A依賴B,B也依賴A,A例項化的時候,發現依賴B,則遞迴去例項化B,B發現依賴A,則遞迴例項化A,此時會走到原點A的例項化,第一次A的例項化還沒完成,只不過把例項化的物件加入到快取中,但是狀態還是正在建立中,由此回到原點發現A正在建立中,由此可以判斷是迴圈依賴了
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //如果此bean正在載入,則不處理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //當某些方法需要提前初始化的時候會直接呼叫addSingletonFactory把對應的ObjectFactory初始化策略儲存在singletonFactory中
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //使用預先設定的getObject方法
                    singletonObject = singletonFactory.getObject();
                    記錄在快取中,注意earlySingletonObjects和singletonFactories是互斥的
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

接下來我們根據原始碼再來梳理下這個方法,這樣更易於理解,這個方法先嚐試從singletonObjects裡面獲取例項,如果如果獲取不到再從earlySingletonObjects裡面獲取,如果還獲取不到,再嘗試從singletonFactories裡面獲取beanName對應的ObjectFactory,然後再呼叫這個ObjectFactory的getObject方法建立bean,並放到earlySingletonObjects裡面去,並且從singletonFactoryes裡面remove調這個ObjectFactory,而對於後續所有的記憶體操作都只為了迴圈依賴檢測時候使用,即allowEarlyReference為true的時候才會使用。
這裡涉及到很多個儲存bean的不同map,簡單解釋下:
singletonObjects:用於儲存BeanName和建立bean例項之間的關係,beanName–>bean Instance
singletonFactories:用於儲存BeanName和建立bean的工廠之間的關係,banName–>ObjectFactory
earlySingletonObjects:也是儲存BeanName和建立bean例項之間的關係,與singletonObjects的不同之處在於,當一個單例bean被放到這裡面後,那麼當bean還在建立過程中,就可以通過getBean方法獲取到了,其目的是用來檢測迴圈引用。
registeredSingletons:用來儲存當前所有已註冊的bean.

從bean的例項中獲取物件

獲取到bean以後就要獲取例項物件了,這裡用到的是getObjectForBeanInstance方法。getObjectForBeanInstance是個頻繁使用的方法,無論是從快取中獲得bean還是根據不同的scope策略載入bean.總之,我們得到bean的例項後,要做的第一步就是呼叫這個方法來檢測一下正確性,其實就是檢測獲得Bean是不是FactoryBean型別的bean,如果是,那麼需要呼叫該bean對應的FactoryBean例項中的getObject()作為返回值。接下來我們看下此方法的原始碼:

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    //如果指定的name是工廠相關的(以&開頭的)
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        //如果是NullBean則直接返回此bean
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        //如果不是FactoryBean型別,則驗證不通過丟擲異常
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), 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.
    //如果獲取的beanInstance不是FactoryBean型別,則說明是普通的Bean,可直接返回
    //如果獲取的beanInstance是FactoryBean型別,但是是以(以&開頭的),也直接返回,此時返回的是FactoryBean的例項
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }
    Object object = null;
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        //到了這裡說明獲取的beanInstance是FactoryBean型別,但沒有以"&"開頭,此時就要返回factory內部getObject裡面的物件了
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

接著我們來看看真正的核心功能getObjectFromFactoryBean(factory, beanName, !synthetic)方法中實現的,繼續跟進程式碼:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 為單例模式且快取中存在
    if (factory.isSingleton() && containsSingleton(beanName)) {

        synchronized (getSingletonMutex()) {
            // 從快取中獲取指定的 factoryBean
            Object object = this.factoryBeanObjectCache.get(beanName);

            if (object == null) {
                // 為空,則從 FactoryBean 中獲取物件
                object = doGetObjectFromFactoryBean(factory, beanName);

                // 從快取中獲取
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    // 需要後續處理
                    if (shouldPostProcess) {
                        // 若該 bean 處於建立中,則返回非處理物件,而不是儲存它
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        // 前置處理
                        beforeSingletonCreation(beanName);
                        try {
                            // 對從 FactoryBean 獲取的物件進行後處理
                            // 生成的物件將暴露給bean引用
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            // 後置處理
                            afterSingletonCreation(beanName);
                        }
                    }
                    // 快取
                    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 例項物件中的核心方法之一了。這裡我們關注三個方法:beforeSingletonCreation() 、 afterSingletonCreation() 、 postProcessObjectFromFactoryBean()。可能有小夥伴覺得前面兩個方法不是很重要,LZ 可以肯定告訴你,這兩方法是非常重要的操作,因為他們記錄著 bean 的載入狀態,是檢測當前 bean 是否處於建立中的關鍵之處,對解決 bean 迴圈依賴起著關鍵作用。before 方法用於標誌當前 bean 處於建立中,after 則是移除。其實在這篇部落格剛剛開始就已經提到了 isSingletonCurrentlyInCreation() 是用於檢測當前 bean 是否處於建立之中,如下:

public boolean isSingletonCurrentlyInCreation(String beanName) {
    return this.singletonsCurrentlyInCreation.contains(beanName);
}

是根據 singletonsCurrentlyInCreation 集合中是否包含了 beanName,集合的元素則一定是在 beforeSingletonCreation() 中新增的,如下:

protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

afterSingletonCreation() 為移除,則一定就是對 singletonsCurrentlyInCreation 集合 remove 了,如下:

protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
}

我們再來看看真正的核心方法 doGetObjectFromFactoryBean

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
        throws BeanCreationException {

    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    // Do not accept a null value for a FactoryBean that's not fully
    // initialized yet: Many FactoryBeans just return null then.
    if (object == null) {
        if (isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(
                    beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        object = new NullBean();
    }
    return object;
}

以前我們曾經介紹過FactoryBean的呼叫方法,如果bean宣告為FactoryBean型別,則當提取bean時候提取的不是FactoryBean,而是FactoryBean中對應的getObject方法返回的bean,而doGetObjectFromFactroyBean真是實現這個功能。

而呼叫完doGetObjectFromFactoryBean方法後,並沒有直接返回,getObjectFromFactoryBean方法中還呼叫了object = postProcessObjectFromFactoryBean(object, beanName);方法,在子類AbstractAutowireCapableBeanFactory,有這個方法的實現:

@Override
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
    return applyBeanPostProcessorsAfterInitialization(object, beanName);
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

對於後處理器的使用,我們目前還沒接觸,後續會有大量篇幅介紹,這裡我們只需要瞭解在Spring獲取bean的規則中有這樣一條:儘可能保證所有bean初始化後都會呼叫註冊的BeanPostProcessor的postProcessAfterInitialization方法進行處理,在實際開發過程中大可以針對此特性設計自己的業務處理。

 

相關文章