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

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

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

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

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

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

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

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

1. BeanDefinition的Resource定位

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

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

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

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

編程式使用DefaultListableBeanFactory容器

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

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

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

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

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

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

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

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

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

圖1.1.2 FileSystemXmlApplicationContext中用于從文件系統中載入Resource的方法

圖1.1.3 FileSystemXmlApplicationContext中含有refresh方法的構造函數
圖1.1.4 我們可以發現,在FileSystemXmlApplicationContext中不管調用哪個構造函數,最終都是會調用這個包含了refresh方法的構造函數,因此我們可以很容易得出結論:觸發對BeanDefinition資源定位過程的refresh方法的調用是在FileSystemXmlApplicationContext的構造函數中啟動的。
圖1.1.5 getResrouceByPath的調用關系棧

根據對上圖調用關系以及FileSystemXmlApplicationContext中含有refresh方法的構造函數的分析,我們可以清楚地看到整個BeanDefinition資源定位的過程。這個對BeanDefinition資源定位的過程,最初是由refresh來觸發的,這個refresh的調用是在FileSystemXmlApplicationContext的構造函數中啟動的。

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

圖1.1.6 AbstractRefershableApplicationContext中的refreshBeanFactory方法
圖1.1.7

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

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

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

圖1.1.9 AbstractBeanDefinitionReader中的loadBeanDefinitions方法

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

圖1.10 DefaultResourceLoader中的getResource方法

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

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

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

2. BeanDefinition的載入和解析

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

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

圖1.2.1 啟動BeanDefinition的載入

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

圖1.2.2 refresh方法

3. BeanDefinition在IoC容器中的注冊

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

圖1.2.3 DefaultListableBeanFactory中用于持有BeanDefinition的ConcurrentHashMap

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

BeanDefinition注冊的實現

圖1.2.4 用于BeanDefinition注冊的registerBeanDefinition方法.png
圖1.2.5 registerBeanDefinition方法的調用關系(是在XmlBeanDefinitionRead中的loadBeanDefinitions觸發BeanDefinition注冊的).png

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

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

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

依賴注入主要發生在兩個階段

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

1.getBean觸發的依賴注入

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

//這里是對BeanFactory接口的實現,比如說getBean方法
//這些getBean接口方法最終是通過調用doGetBean來實現的
@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的地方,也就是觸發依賴注入的地方 
    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,處理那些已經被創建過的單例Bean,這種Bean不要重復創建
        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的相關處理,以取得FactoryBean
           //的生產結果,BeanFactory和FactoryBean的區別我在后面會講
            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是否存在,若在當前工廠不存在則去順著雙親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 {
                 //根據Bean的名字取得BeanDefinition  
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // 遞歸獲得當前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);
                    }
                }

                // 通過調用createBean方法創建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);
                }
                //這里是創建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;
            }
        }

        // 這里對創建的Bean進行類型檢查,如果沒有問題,就返回這個新創建的Bean,這個Bean已經是包含了依賴關系的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;
    }

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

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

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

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

  • 生產Bea所包含的Java對象;
  • Bean對象生成之后,把這些Bean對象的依賴關系設置好。

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

生成Bean所包含的Java對象

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

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // 確認需要創建的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進行實例化
        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);
            }
        }

        // 使用構造函數對Bean進行實例化
        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) {
    //使用默認的實例化策略對Bean進行實例化,默認的實例化策略是
       //CglibSubclassingInstantiationStrategy,也就是實用CGLIB來對Bean進行實例化    
    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進行實例化。CGLIB是一個字節碼生成器的類庫,它提供了一系列的API來提供生成和轉換Java的字節碼的功能。在Spring AOP中也使用CGLIB對Java的字節碼進行增強。在IoC容器中,要了解怎樣使用CGLIB來生成Bean對象,需要看一下SimpleInstantiationStrategy類。這個Strategy是Spring用來生成Bean對象的默認類,它提供了兩種實例化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()) {
           //這里取得指定的構造器或者生成對象的工廠方法來對Bean進行實例化
            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進行實例化,這個BeanUtils的實例化通過Constructor來實例化Bean
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // 使用CGLIB來實例化對象
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }
Bean之間依賴關系的處理

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

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

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

2. lazy-init屬性和預實例化

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容