Spring源碼探究:IoC容器初始化過程詳解

寫在前面:我是直接通過閱讀Spring源碼并參考《Spring技術(shù)內(nèi)幕(第2版)》一書來對Spring核心設(shè)計思想進(jìn)行學(xué)習(xí)的。但是在學(xué)習(xí)的過程當(dāng)中,我發(fā)現(xiàn)一個問題:書中為了能夠用精簡篇幅,采用了將部分父類中的代碼放到子類當(dāng)中的形式來進(jìn)行講解。這樣會導(dǎo)致我們在查看某個類的時候發(fā)現(xiàn)并不存在書中提到的某些方法,這個時候就要靈活應(yīng)用函數(shù)的調(diào)用棧以及類的繼承關(guān)系回溯到該類的基類去查看某些方法。

在之前的博客Spring源碼探究:IoC容器在Web容器中的初始化中,我已經(jīng)介紹了IoC容器在Web容器中是如何被創(chuàng)建并進(jìn)行初始化的。但是對初始化的介紹僅僅停留在文末對configureAndRefreshWebApplicationContext方法介紹的層面,并沒有詳細(xì)探究IoC容器初始化過程中涉及到的BeanDefinition的Resource定位、BeanDefinition的載入和解析以及BeanDefinition在IoC容器中的注冊等部分。下面,我將參照Spring源碼來對IoC容器初始化過程中涉及到的一些主要階段進(jìn)行介紹;

在炎熱的天氣下閱讀大段大段的源碼確實能算得上是一件令人十分心煩的事情了

在進(jìn)一步探究之前,我們首先明確以下三點:

  • 下文中的“容器”如果沒有特別說明的話,一律認(rèn)為是"IoC容器";
  • 嚴(yán)格來說,容器的初始化過程主要是包括三個部分:BeanDefinition的Resource定位、BeanDefinition的載入和解析以及BeanDefinition在容器中的注冊。至于容器的依賴注入,其實是發(fā)生在用戶第一次向容器索要Bean時觸發(fā)的,即用戶調(diào)用getBean的時候?qū)⒂|發(fā)容器中依賴注入的過程。但是也有例外的時候,也就是我們可以在BeanDefinition中通過對lazy-init屬性的設(shè)置來讓容器完成對Bean的預(yù)實例化(其實也就是依賴注入的過程)。所以本文介紹的容器初始化過程中也會包含關(guān)于依賴注入的細(xì)節(jié)。
  • 由于我們探究的是IoC容器初始化的過程。在初始化的過程當(dāng)中,我們會看到一個又一個的函數(shù)被調(diào)用。換句話說,其實是通過調(diào)用一些函數(shù)來完成IoC容器初始化的。因此,在本文中我會借助大量的函數(shù)調(diào)用棧(如圖1.1.5、圖1.1.7等等)來捋清整個過程當(dāng)中發(fā)生了什么。如果你在閱讀本文的過程中感覺思維混亂,請根據(jù)我所貼出的函數(shù)調(diào)用關(guān)系來閱讀文章。

Spring源碼探究:IoC容器在Web容器中的初始化文末提到了一個refresh()方法,這個方法其實標(biāo)志著IoC容器初始化過程的正式啟動。具體地來說,這個啟動包括BeanDefinition的Resrouce定位、載入和注冊三個基本過程。Spring之所以把這三個基本過程分開,并使用不同的模塊來完成,如使用響應(yīng)的ResourceLoader、BeanDefinitionReader等模塊,通過這樣的設(shè)計方式,可以讓我們更加靈活地對這三個過程進(jìn)行裁剪或拓展,定義出最適合自己的IoC容器的初始化過程。

第一部分 IoC容器的初始化過程

1. BeanDefinition的Resource定位

我們一直在說BeanDefinition,那么它到底是什么東西呢?
從字面上理解,它代表著Bean的定義。其實,它就是完整的描述了在Spring配置文件中定義的節(jié)點中所有信息,包括各種子節(jié)點。不太恰當(dāng)?shù)卣f,我們可以把它理解為配置文件中一個個<bean></bean>節(jié)點所包含的信息。

我們將以編程式使用DefaultListableBeanFactory來引入BeanDefinition的Resource定位
先介紹一下DefaultListableBeanFactory:

DefaultListableBeanFactory非常重要,是我們經(jīng)常要用到的一個IoC容器的實現(xiàn),比如在設(shè)計應(yīng)用上下文ApplicationContext時就會用到它。這個DefaultListableBeanFactory實際上包含了基本IoC容器所具有的重要功能,也是在很多地方都會用到的容器系列中的一個基本產(chǎn)品。
在Spring中,實際上是把DefaultListableBeanFactory作為一個默認(rèn)的功能完整的IoC容器來使用的。

圖1.1.1 XmlBeanFactory就是在繼承了DefaultListableBeanFactory容器功能的基礎(chǔ)上增加了新的功能

編程式使用DefaultListableBeanFactory容器

ClassPathResource res = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinition(res);

如此,我們就可以通過factory對象來使用DefaultListableBeanFactory這個IoC容器了。
在使用IoC容器的時候,需要以下幾個步驟:

  • 創(chuàng)建IoC配置文件的抽象資源,這個抽象資源包含了BeanDefinition的定義信息,這里使用的是ClassPathResource
  • 創(chuàng)建一個BeanFactory,這里使用的是DefaultListableBeanFactory;
  • 創(chuàng)建一個載入BeanDefinition的讀取器,這里使用的是XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition,然后通過一個回調(diào)配置給BeanFactory。
  • 從定義好的資源位置讀取配置信息,具體的解析過程由BeanDefinitionReader來完成。完成整個在如何注冊Bean定義之后,需要的IoC容器就建立起來了。這個時候就可以直接使用IoC容器了。

從上面可以得知,以編程的方式使用DefaultListableBeanFactory時,首先定義一個Resource來定位容器使用的BeanDefinition。這時使用的是ClassPathResource,這意味著Spring會在類路徑中去尋找以文件形式存在的BeanDefinition信息。

ClassPathResource resource = new ClassPathResource("bean.xml");

這里定義的BeanDefinition并不能由DefaultListableBeanFactory直接使用,Spring需要通過BeanDefinitionReader來對這些信息進(jìn)行處理。

在這里,我們可以看到使用ApplicationContext相對于直接使用DefaultListableBeanFactory的好處。因為在ApplicationContext中,Spring已經(jīng)為我們提供了一系列加載不同Resource的讀取器的實現(xiàn),而DefaultListableBeanFactory只是一個純粹的IoC容器,需要為它提供特定的讀取器才能完成這些功能。但是從另一方面來講,使用DefaultListableBeanFactory這種更底層的容器能提高定制IoC容器的靈活性。

比如說常用的一些ApplicationContext,例如FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext等。從字面意思上我們就能看出它們可以提供哪些不同的Resource讀入功能,比如FileSystemXmlApplicationContext可以從文件系統(tǒng)載入Resource,ClassPathXmlApplicationContext可以從Class Path載入Resource,XmlWebApplicationContext可以在Web容器中載入Resource等。

下面將以FileSystemXmlApplicationContext為例對IoC容器初始化過程進(jìn)行探究

圖1.1.2 FileSystemXmlApplicationContext中用于從文件系統(tǒng)中載入Resource的方法

圖1.1.3 FileSystemXmlApplicationContext中含有refresh方法的構(gòu)造函數(shù)
圖1.1.4 我們可以發(fā)現(xiàn),在FileSystemXmlApplicationContext中不管調(diào)用哪個構(gòu)造函數(shù),最終都是會調(diào)用這個包含了refresh方法的構(gòu)造函數(shù),因此我們可以很容易得出結(jié)論:觸發(fā)對BeanDefinition資源定位過程的refresh方法的調(diào)用是在FileSystemXmlApplicationContext的構(gòu)造函數(shù)中啟動的。
圖1.1.5 getResrouceByPath的調(diào)用關(guān)系棧

根據(jù)對上圖調(diào)用關(guān)系以及FileSystemXmlApplicationContext中含有refresh方法的構(gòu)造函數(shù)的分析,我們可以清楚地看到整個BeanDefinition資源定位的過程。這個對BeanDefinition資源定位的過程,最初是由refresh來觸發(fā)的,這個refresh的調(diào)用是在FileSystemXmlApplicationContext的構(gòu)造函數(shù)中啟動的。

那么,F(xiàn)ileSystemXmlApplication在什么地方定義了BeanDefinition的讀入器BeanDefinitionReader,從而完成BeanDefinition信息的讀入呢?在前面我們分析過,在IoC容器的初始化過程中,BeanDefinition資源的定位、載入和注冊過程是分開進(jìn)行的,這也是解耦的一個體現(xiàn)。關(guān)于這個讀入器的配置,我們接下來到FileSystemXmlAppplicationContext的基類AbstractRefreshableApplicationContextrefreshBeanFactory方法去看看。

圖1.1.6 AbstractRefershableApplicationContext中的refreshBeanFactory方法
圖1.1.7

剛才我們提到,refresh的調(diào)用是在FileSystemXmlApplicationContext的構(gòu)造函數(shù)中啟動的,而這個refresh又是從AbstractApplicationContext繼承過來的。因此,結(jié)合上面這個函數(shù)調(diào)用關(guān)系圖我們知道:refreshBeanFactory被FileSystemXmlApplicationContext構(gòu)造函數(shù)中的refresh調(diào)用。在這個方法(refreshBeanFactory)中,通過createBeanFactory構(gòu)建了一個IoC容器供ApplicationContext使用。這個IoC容器就是前面我們提到的DefaultListableBeanFactory,同時它還啟動了loadBeanDefinition來載入BeanDefinition,這里和以編程式使用IoC容器的過程很相似。

圖1.1.8 在AbstractRefreshableApplictionContext中,這里是使用BeanDefinitionReader載入Bean定義的地方,因為允許有多種載入方式(雖然常用的是XML定義的形式),這里通過一個抽象函數(shù)把具體的實現(xiàn)委托給子類來完成

我們從圖1.1.5的方法調(diào)用棧可以發(fā)現(xiàn)圖1.6中refreshBeanFactory方法中調(diào)用的loadBeanDefinitions方法其實最終是調(diào)用AbstractBeanDefinitionReader里面的loadBeanDefinitions方法,下面我們直接看一下這個方法。

圖1.1.9 AbstractBeanDefinitionReader中的loadBeanDefinitions方法

從上圖可以得知,Resource的定位工作是由DefaultResourceLoader中的getResource來完成的。所以對于取得Resource的具體過程,我們來看看DefaultResourceLoader是怎樣完成的:

圖1.10 DefaultResourceLoader中的getResource方法

圖1.1.10中使用的getResourcePath方法將會被FileSystemXmlApplicationContext實現(xiàn),如圖1.2所示。該方法返回的是一個FileSystemResource對象,通過這個對象,Spring可以進(jìn)行相關(guān)的I/O操作,完成BeanDefinition的定位。

如果是其他的ApplicationContext,那么會對應(yīng)生成其他種類的Resource,比如ClassPathResource、ServletContextResource等。

所以BeanDefinition的定位到這里就完成了。在BeanDefinition定位完成的基礎(chǔ)上,就可以通過返回的Resource對象進(jìn)行BeanDefinition的載入了。在定位過程完成之后,為BeanDefinition的載入創(chuàng)造了進(jìn)行I/O操作的條件,但是具體的數(shù)據(jù)還沒有開始讀入。這些讀入將在下面介紹的BeanDefinition的載入和解析中來完成。

2. BeanDefinition的載入和解析

在完成對BeanDefinition的Resource定位之后,我們現(xiàn)在來了解整個BeanDefinition信息的載入過程。對于IoC容器來說,載入過程相當(dāng)于把定義的BeanDefinition在IoC容器中轉(zhuǎn)化為一個Spring內(nèi)部數(shù)據(jù)結(jié)構(gòu)的過程。IoC容器對Bean的管理和依賴注入功能的實現(xiàn)是通過對其持有的BeanDefinition進(jìn)行各種相關(guān)的操作來完成的。這些BeanDefinition數(shù)據(jù)在IoC容器中通過一個HashMap來保持和維護(hù)。需要注意的是,這只是一種比較簡單的維護(hù)方式,如果需要提高IoC容器的性能和容量,可以自己做一些拓展。

以DefaultListableBeanFactory的設(shè)計入手來看看IoC容器是怎樣完成BeanDefinition載入的

圖1.2.1 啟動BeanDefinition的載入

對于容器的啟動來說,refresh是一個非常重要的方法。該方法在AbstractApplicationContext類(它是FileSystemXmlApplicationContext的基類)中找到,它詳細(xì)地描述了整個ApplicationContext的初始化過程,比如BeanFactory的更新,MessageSource和PostProcessor的注冊等等。這個執(zhí)行過程為Bean的生命周期提供了條件。

圖1.2.2 refresh方法

3. BeanDefinition在IoC容器中的注冊

BeanDefinition在IoC容器中完成了載入和解析的過程之后,用戶定義的BeanDefinition信息已經(jīng)在IoC容器內(nèi)建立起了自己的數(shù)據(jù)結(jié)構(gòu)以及相應(yīng)的數(shù)據(jù)表示,但此時這些數(shù)據(jù)還不能供IoC容器直接使用,需要在IoC容器中對這些BeanDefinition數(shù)據(jù)進(jìn)行注冊。這個注冊為IoC容器提供了更友好的使用方式,在DefaultListableBeanFactory中,是通過一個ConcurrentHashMap來持有載入的BeanDefinition的。

圖1.2.3 DefaultListableBeanFactory中用于持有BeanDefinition的ConcurrentHashMap

將解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注冊的過程是在載入BeanDefinition完成后進(jìn)行的。

BeanDefinition注冊的實現(xiàn)

圖1.2.4 用于BeanDefinition注冊的registerBeanDefinition方法.png
圖1.2.5 registerBeanDefinition方法的調(diào)用關(guān)系(是在XmlBeanDefinitionRead中的loadBeanDefinitions觸發(fā)BeanDefinition注冊的).png

完成了BeanDefinition的注冊,就完成了整個IoC容器的初始化過程。此時,在使用的IoC容器DefaultListableBeanFactory中已經(jīng)建立了整個Bean的配置信息,而且這些BeanDefinition已經(jīng)可以被容器使用了,它們都在beanDefinitionMap里面被檢索和使用/容器的作用就是對這些信息進(jìn)行處理和維護(hù)。這些信息是容器建立依賴反轉(zhuǎn)的基礎(chǔ),有了這些基礎(chǔ)數(shù)據(jù),我們下面學(xué)習(xí)一些在IoC容器中依賴注入是怎樣完成的。

第二部分 IoC容器的依賴注入

在IoC容器初始化的過程里,主要完成的工作是在IoC容器中建立BeanDefinition數(shù)據(jù)映射,并沒有看到IoC容器對Bean之間的依賴關(guān)系進(jìn)行注入。

依賴注入主要發(fā)生在兩個階段

  • 正常情況下,依賴注入的過程是用戶第一次向IoC容器索要Bean時觸發(fā)的。
  • 但是我們可以在BeanDefinition信息中通過控制lazy-init屬性來讓容器完成對Bean的預(yù)實例化,即在初始化的過程中就完成某些Bean的依賴注入工作。

1.getBean觸發(fā)的依賴注入

在基本的IoC容器接口BeanFactory中,有一個getBean的接口定義,這個接口的實現(xiàn)就是觸發(fā)依賴注入發(fā)生的地方。為了進(jìn)一步了解這個依賴注入的過程,我們從DefaultListableBeanFactory的基類AbstractBeanFactory入手去看看getBean的實現(xiàn)。

//這里是對BeanFactory接口的實現(xiàn),比如說getBean方法
//這些getBean接口方法最終是通過調(diào)用doGetBean來實現(xiàn)的
@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);
    }

    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * @param name the name of the bean to retrieve
     * @param requiredType the required type of the bean to retrieve
     * @param args arguments to use when creating a bean instance using explicit arguments
     * (only applied when creating a new instance as opposed to retrieving an existing one)
     * @return an instance of the bean
     * @throws BeansException if the bean could not be created
     */
    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        return doGetBean(name, requiredType, args, false);
    }
        //這里是實際取得Bean的地方,也就是觸發(fā)依賴注入的地方 
    protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

        final String beanName = transformedBeanName(name);
        Object bean;

        // 先從緩存中取得Bean,處理那些已經(jīng)被創(chuàng)建過的單例Bean,這種Bean不要重復(fù)創(chuàng)建
        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 + "'");
                }
            }
            //這里的getObjectForBeanInstance完成的是FactoryBean的相關(guān)處理,以取得FactoryBean
           //的生產(chǎn)結(jié)果,BeanFactory和FactoryBean的區(qū)別我在后面會講
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // 檢查IoC容器中的BeanDefinition是否存在,若在當(dāng)前工廠不存在則去順著雙親BeanFactory鏈一直向上找
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                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);
                }
            }

            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                 //根據(jù)Bean的名字取得BeanDefinition  
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // 遞歸獲得當(dāng)前Bean依賴的所有Bean(如果有的話)
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dependsOnBean : dependsOn) {
                        if (isDependent(beanName, dependsOnBean)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
                        }
                        registerDependentBean(dependsOnBean, beanName);
                        getBean(dependsOnBean);
                    }
                }

                // 通過調(diào)用createBean方法創(chuàng)建Singlton bean實例
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            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);
                }
                //這里是創(chuàng)建prototype bean的地方
                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);
                }

                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                            @Override
                            public Object getObject() throws BeansException {
                                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;
            }
        }

        // 這里對創(chuàng)建的Bean進(jìn)行類型檢查,如果沒有問題,就返回這個新創(chuàng)建的Bean,這個Bean已經(jīng)是包含了依賴關(guān)系的Bean
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
            try {
                return getTypeConverter().convertIfNecessary(bean, requiredType);
            }
            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;
    }

依賴注入就是在這里被觸發(fā)的。而依賴注入的發(fā)生是在容器中的BeanDefinition數(shù)據(jù)已經(jīng)建立好的前提下進(jìn)行的。雖然我們可以用最簡單的方式來描述IoC容器,那就是視其為一個HashMap,但只能說這個HashMap是容器的最基本的數(shù)據(jù)結(jié)構(gòu),而不是IoC容器的全部。

getBean是依賴注入的起點,之后會調(diào)用AbstractAutowireCapableBeanFactory中的createBean來生產(chǎn)需要的Bean,并且還對Bean初始化進(jìn)行處理,比如實現(xiàn)了在BeanDefinition中的init-method屬性定義,Bean后置處理器等。

在這里對于依賴注入的所有細(xì)節(jié)就不一一介紹了,下面挑重點講

依賴注入其實包括兩個主要過程:

  • 生產(chǎn)Bea所包含的Java對象;
  • Bean對象生成之后,把這些Bean對象的依賴關(guān)系設(shè)置好。

與依賴注入關(guān)系特別密切的方法有createBeanInstancepopulateBean。前者用于生成Bean所包含的對象,后者主要是用來處理對各種Bean對象的屬性進(jìn)行處理的過程(即依賴關(guān)系處理的過程)。
所以下面我們針對這兩個過程分別來學(xué)習(xí)。

生成Bean所包含的Java對象

在createBeanInstance中生成了Bean所包含的對象,這個對象的生成有很多種不同的方式,可以通過工廠方法生成,也可以通過容器的autowire特性生成,這些生成方法都是由相關(guān)的BeanDefinition來指定的。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 確認(rèn)需要創(chuàng)建的Bean實例的類可以實例化
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }
        //這里使用工廠方法對Bean進(jìn)行實例化
        if (mbd.getFactoryMethodName() != null)  {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        // Shortcut when re-creating the same bean...
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                return instantiateBean(beanName, mbd);
            }
        }

        // 使用構(gòu)造函數(shù)對Bean進(jìn)行實例化
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }
//最常見的實例化過程instantiateBean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    //使用默認(rèn)的實例化策略對Bean進(jìn)行實例化,默認(rèn)的實例化策略是
       //CglibSubclassingInstantiationStrategy,也就是實用CGLIB來對Bean進(jìn)行實例化    
    try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        return getInstantiationStrategy().instantiate(mbd, beanName, parent);
                    }
                }, getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

這里使用了CGLIB對Bean進(jìn)行實例化。CGLIB是一個字節(jié)碼生成器的類庫,它提供了一系列的API來提供生成和轉(zhuǎn)換Java的字節(jié)碼的功能。在Spring AOP中也使用CGLIB對Java的字節(jié)碼進(jìn)行增強(qiáng)。在IoC容器中,要了解怎樣使用CGLIB來生成Bean對象,需要看一下SimpleInstantiationStrategy類。這個Strategy是Spring用來生成Bean對象的默認(rèn)類,它提供了兩種實例化Bean對象的方法:一種是通過BeanUtils,它使用了JVM的反射功能,另一種是通過CGLIB來生成。

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (bd.getMethodOverrides().isEmpty()) {
           //這里取得指定的構(gòu)造器或者生成對象的工廠方法來對Bean進(jìn)行實例化
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                @Override
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Exception ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            //通過BeanUtils進(jìn)行實例化,這個BeanUtils的實例化通過Constructor來實例化Bean
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // 使用CGLIB來實例化對象
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }
Bean之間依賴關(guān)系的處理

依賴關(guān)系處理的入口是前面提到的populateBean方法。由于其中涉及的面太多,在這里就不貼代碼了。簡要介紹一下依賴關(guān)系處理的流程:在populateBean方法中,首先取得在BeanDefinition中設(shè)置的property值,然后開始依賴注入的過程。首先處理autowire的注入,可以byName或者是byType,之后對屬性進(jìn)行注入。接著需要對Bean Reference進(jìn)行解析,在對ManageList、ManageSet、ManageMap等進(jìn)行解析完之后,就已經(jīng)為依賴注入準(zhǔn)備好了條件,這是真正把Bean對象設(shè)置到它所依賴的另一個Bean屬性中去的地方,其中處理的屬性是各種各樣的。依賴注入發(fā)生在BeanWrapper的setPropertyValues中,具體的完成卻是在BeanWrapper的子類BeanWrapperImpl中實現(xiàn)的,它會完成Bean的屬性值的注入,其中包括對Array的注入、對List等集合類以及對非集合類的域進(jìn)行注入。進(jìn)過一系列的注入,這樣就完成了對各種Bean屬性的依賴注入過程。

在Bean的創(chuàng)建和對象依賴注入的過程中,需要依據(jù)BeanDefinition中的信息來遞歸地完成依賴注入。從前面的幾個遞歸過程中可以看到,這些遞歸都是以getBean為入口的。一個遞歸是在上下文體系中查找需要的Bean和創(chuàng)建Bean的遞歸調(diào)用;另一個遞歸是在依賴注入時,通過遞歸調(diào)用容器的getBean方法,得到當(dāng)前Bean的依賴Bean,同時也觸發(fā)對依賴Bean的創(chuàng)建和注入。在對Bean的屬性進(jìn)行依賴注入時,解析的過程也是一個遞歸的過程。這樣,根據(jù)依賴關(guān)系,一層層地完成Bean的創(chuàng)建和注入,直到最后完成當(dāng)前Bean的創(chuàng)建。有了這個頂層Bean的創(chuàng)建和對它屬性依賴注入的完成,意味著和當(dāng)前Bean相關(guān)的整個依賴鏈的注入液完成了。

在Bean創(chuàng)建和依賴注入完成以后,在IoC容器中建立起一系列依靠依賴關(guān)系聯(lián)系起來的Bean,這個Bean已經(jīng)不再是簡單的Java對象了。該Bean系列以及Bean之間的依賴關(guān)系建立完成之后,通過IoC的相關(guān)接口方法,就可以非常方便地供上層應(yīng)用使用了。

2. lazy-init屬性和預(yù)實例化

在圖1.2.2的refresh方法中,我們可以看到調(diào)用了finishBeanFactoryInitialization來對配置了lazy-init的Bean進(jìn)行處理。其實在這個方法中,封裝了對lazy-init屬性的處理,實際的處理是在DefaultListableBeanFactory這個基本容器的preInstantiateSingleton方法中完成的。該方法對單例Bean完成預(yù)實例化,這個預(yù)實例化的完成巧妙地委托給容器來實現(xiàn)。如果需要預(yù)實例化,那么就直接在這里采用getBean去觸發(fā)依賴注入,與正常依賴注入的觸發(fā)相比,只有觸發(fā)的時間和場合不同。在這里,依賴注入發(fā)生在容器執(zhí)行refresh的過程中,即IoC容器初始化的過程中,而不像一般的依賴注入一樣發(fā)生在IoC容器初始化完成以后,第一次通過getBean想容器索要Bean的時候。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容