Spring學習之——Bean載入流程

落叶已归根發表於2024-05-06

Spring IOC容器就像是一個生產產品的流水線上的機器,Spring建立出來的Bean就好像是流水線的終點生產出來的一個個精美絕倫的產品。既然是機器,總要先啟動,Spring也不例外。因此Bean的載入流程總體上來說可以分為兩個階段:

  • 容器啟動階段
  • Bean建立階段

一、容器啟動階段:

容器的啟動階段做了很多的預熱工作,為後面Bean的例項化做好了充分的準備,我們首先看一下容器的啟動階段都做了哪些預熱工作。

1、配置元資訊:

Spring IOC容器將物件例項的建立與物件例項的使用分離,當業務中需要依賴某個物件,不再依靠我們自己手動建立,只需向Spring要,Spring就會以注入的方式交給我們需要的依賴物件。既然將物件建立的任務交給了Spring,那麼Spring就需要知道建立一個物件所需要的一些必要的資訊。而這些必要的資訊可以是Spring過去支援最完善的xml配置檔案,或者是其他形式的例如properties的磁碟檔案,也可以是現在主流的註解,甚至是直接的程式碼硬編碼。總之,這些建立物件所需要的必要資訊稱為配置元資訊。

<bean id="role" class="com.wbg.springxmlbean.entity.Role">
    <!-- property元素是定義類的屬性,name屬性定義的是屬性名稱 value是值
    相當於:
    Role role=new Role();
    role.setId(1);
    role.setRoleName("高階工程師");
    role.setNote("重要人員");-->
    <property name="id" value="1"/>
    <property name="roleName" value="高階工程師"/>
    <property name="note" value="重要人員"/>
</bean>

2、BeanDefination:

在Java世界中,萬物皆物件,散落於程式程式碼各處的註解以及儲存在磁碟上的xml或者其他檔案等等配置元資訊,在記憶體中總要以一種物件的形式表示,就好比我們活生生的人對應到Java世界中就是一個Person類,而Spring選擇在記憶體中表示這些配置元資訊的方式就是BeanDefination,這裡我們不會去分析BeanDefination的程式碼,這裡我們只需要知道配置元資訊被載入到記憶體之後是以BeanDefination的形存在的即可。

3、BeanDefinationReader:

Spring是如何看懂這些配置元資訊的呢?這個就要靠我們的BeanDefinationReader了。不同的BeanDefinationReader擁有不同的功能,如果我們要讀取xml配置元資訊,那麼可以使用XmlBeanDefinationReader。如果我們要讀取properties配置檔案,那麼可以使用PropertiesBeanDefinitionReader載入。而如果我們要讀取註解配置元資訊,那麼可以使用 AnnotatedBeanDefinitionReader載入。我們也可以很方便的自定義BeanDefinationReader來自己控制配置元資訊的載入。總的來說,BeanDefinationReader的作用就是載入配置元資訊,並將其轉化為記憶體形式的BeanDefination,存在某一個地方,至於這個地方在哪裡,不要著急,接著往下看!

4、BeanDefinationRegistry:

執行到這裡,Spring已經將存在於各處的配置元資訊載入到記憶體,並轉化為BeanDefination的形式,這樣我們需要建立某一個物件例項的時候,找到相應的BeanDefination然後建立物件即可。那麼我們需要某一個物件的時候,去哪裡找到對應的BeanDefination呢?這種透過Bean定義的id找到物件的BeanDefination的對應關係或者說對映關係又是如何儲存的呢?這就引出了BeanDefinationRegistry了。

Spring透過BeanDefinationReader將配置元資訊載入到記憶體生成相應的BeanDefination之後,就將其註冊到BeanDefinationRegistry中,BeanDefinationRegistry就是一個存放BeanDefination的大籃子,它也是一種鍵值對的形式,透過特定的Bean定義的id,對映到相應的BeanDefination。

5、BeanFactoryPostProcessor:

BeanFactoryPostProcessor是容器啟動階段Spring提供的一個擴充套件點,主要負責對註冊到BeanDefinationRegistry中的一個個的BeanDefination進行一定程度上的修改與替換。例如我們的配置元資訊中有些可能會修改的配置資訊散落到各處,不夠靈活,修改相應配置的時候比較麻煩,這時我們可以使用佔位符的方式來配置。例如配置Jdbc的DataSource連線的時候可以這樣配置:

<bean id="dataSource"  
    class="org.apache.commons.dbcp.BasicDataSource"  
    destroy-method="close">  
    <property name="maxIdle" value="${jdbc.maxIdle}"></property>  
    <property name="maxActive" value="${jdbc.maxActive}"></property>  
    <property name="maxWait" value="${jdbc.maxWait}"></property>  
    <property name="minIdle" value="${jdbc.minIdle}"></property>  
  
    <property name="driverClassName"  
        value="${jdbc.driverClassName}">  
    </property>  
    <property name="url" value="${jdbc.url}"></property>  
  
    <property name="username" value="${jdbc.username}"></property>  
    <property name="password" value="${jdbc.password}"></property>  
</bean> 

BeanFactoryPostProcessor就會對註冊到BeanDefinationRegistry中的BeanDefination做最後的修改,替換$佔位符為配置檔案中的真實的資料。

至此,整個容器啟動階段就算完成了,容器的啟動階段的最終產物就是註冊到BeanDefinationRegistry中的一個個BeanDefination了,這就是Spring為Bean例項化所做的預熱的工作。讓我們再透過一張圖的形式回顧一下容器啟動階段都是搞了什麼事吧。

二、Bean的獲取階段:

在容器啟動階段,已經完成了bean的註冊。如果該物件是配置成懶載入的方式,那麼直到我們向Spring要依賴物件例項之前,其都是以BeanDefinationRegistry中的一個個的BeanDefination的形式存在,也就是Spring只有在我們第一次依賴物件的時候才開啟相應物件的例項化階段。而如果我們不是選擇懶載入的方式,容器啟動階段完成之後,其中有一個步驟finishBeanFactoryInitialization(),在這一步將立即啟動Bean例項化階段,透過隱式的呼叫所有依賴物件的getBean方法來例項化所有配置的Bean,完成類的載入。

1、doGetBean():bean的獲取:

doGetBean()的總體功能就是在建立bean物件之前,先去快取或者beanFactory工廠中檢視是否存在bean,如果存在,則返回,不存在,則進行對應的建立流程。我們先找到doGetBean()方法的入口,閱讀一下這個方法的原始碼:

首先呼叫 ApplicationContext.getBean("beanName")獲取Bean物件:

applicationContext.getBean("name");

然後再呼叫AbstractApplicationContext.getBean("beanName"):

    public Object getBean(String name) throws BeansException {
        assertBeanFactoryActive(); 
        // 呼叫getBean 進入AbstractBeanFactory    
        return getBeanFactory().getBean(name);
    }

再呼叫AbstractBeanFactory類下面的doGetBean()方法:

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
 
        /*
        1、轉換beanName(別名轉換):傳入的引數name可能只是別名,也可能是FactoryBean,所以需要進行解析轉換:
        (1)消除修飾符,比如工廠引用字首 String FACTORY_BEAN_PREFIX = "&";
        (2)解決spring中alias標籤的別名問題
        */ 
        final String beanName = transformedBeanName(name);
        Object bean;
 
        //2、嘗試從快取中去載入例項,如果獲取到了就直接返回
        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
        //如果快取中存在對應的bean
        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 + "'");
                }
            }
            
            //3、快取渠道的bean的例項化。從快取中獲取的bean是原始狀態的bean,需要在這裡對bean進行bean例項化。
            // 此時會進行 合併RootBeanDefinition、BeanPostProcessor進行例項前置處理、例項化、例項後置處理。
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        // 如果快取中沒有對應bean
        else { 
            //4、迴圈依賴檢查。 (構造器的迴圈依賴)迴圈依賴存在,則報錯。
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
 
            // 5、如果快取中沒有資料,就會轉到父類工廠去載入
            //獲取父工廠
            // Check if bean definition exists in this factory.
            BeanFactory parentBeanFactory = getParentBeanFactory();
            
            //!containsBeanDefinition(beanName)就是檢測如果當前載入的xml配置檔案中不包含beanName所對應的配置,就只能到parentBeanFacotory去嘗試載入bean。
            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);
            }
 
            //6、儲存XML配置檔案的GernericBeanDefinition轉換成RootBeanDefinition,即為合併父類定義。
            try {
                // XML配置檔案中讀取到的bean資訊是儲存在GernericBeanDefinition中的,但Bean的後續處理是針對於RootBeanDefinition的,所以需要轉換後才能進行後續操作。
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);
 
                // Guarantee initialization of beans that the current bean depends on.
                //7、初始化依賴的bean
                String[] dependsOn = mbd.getDependsOn();
                //bean中可能依賴了其他bean屬性,在初始化bean之前會先初始化這個bean所依賴的bean屬性。
                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);
                    }
                }
 
                //8、建立bean
                // Create bean instance.
                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);
                }
 
                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 name '" + 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;
            }
        }
 
        // Check if required type matches the type of the actual bean instance.
        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;
    }
 

2、從快取中獲取單例bean:getSingleton(String beanName, boolean allowEarlyReference)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從快取池中獲取bean:singletonObjects 一級緩
    Object singletonObject = this.singletonObjects.get(beanName);
    
    // 如果一級快取中為null,再判斷是否正在建立
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 加鎖,防止併發建立
        synchronized (this.singletonObjects) {
            // 從二級快取中獲取bean,如果此 bean 正在載入則不處理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 當某些方法需要提前初始化,呼叫 addSingletonFactory 方法將對應的objectFactory 初始化策略儲存在 singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

在 Spring 中,bean預設都是單例的,同一個容器的單例只會被建立一次,後續再獲取 bean 時,直接從單例快取 singletonObjects 中進行獲取。而且因為單例快取是公共變數,所以對它進行操作的時候,都進行了加鎖操作,避免了多執行緒併發修改或讀取的覆蓋操作。

還有這裡有個 earlySingletonObjects 變數,它也是單例快取,也是用來儲存 beanName 和 建立 bean 例項之間的關係。與 singletonFactories 不同的是,當一個單例 bean 被放入到這 early 單例快取後,就要從 singletonFactories 中移除,兩者是互斥的,主要用來解決迴圈依賴的問題。

3、從Bean的例項中獲取物件:getObjectForBeanInstance()

在 getBean()方法中,getObjectForBeanInstance() 是個高頻方法,在單例快取中獲得 bean 還是 根據不同的 scope 策略載入 bean,都有這個這個方法的出現。因為從快取中獲取的bean是原始狀態的bean,需要在這裡對bean進行bean例項化。

方法流程小結:

  • (1)驗證 bean 型別:判斷是否是工廠bean
  • (2)對非 FactoryBean 不做處理
  • (3)處理 FactoryBean:如果是FactoryBean型別,先對 bean 進行轉換,再委託給 getObjectFromFactoryBean()方法進行處理。

在這個方法中,對工廠 bean 有特殊處理,最終獲取的是 FactoryBean.getObject() 方法返回的型別。

4、獲取單例:getSingleton(String beanName, ObjectFactory<?> singletonFactory)

// Create bean instance. 建立 bean 例項
// singleton 單例模式(最常使用)
if (mbd.isSingleton()) {
    // 第二個引數的回撥介面,介面是 org.springframework.beans.factory.ObjectFactory#getObject
    // 介面實現的方法是 createBean(beanName, mbd, args)
    sharedInstance = getSingleton(beanName, () -> {
        return createBean(beanName, mbd, args);
        // 省略了 try / catch
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

來看 getSingleton() 方法做了什麼:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 註釋 4.7 全域性變數,加鎖
    synchronized (this.singletonObjects) {
        //1、再次檢查是否已經被載入了,單例模式就是可以複用已經建立的 bean
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            //2、初始化前操作,校驗是否 beanName 是否有別的執行緒在初始化,並記錄beanName的初始化狀態
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            //3、呼叫createBean方法例項化bean
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = null;
            }
            //4、初始化後的操作,移除初始化狀態
            afterSingletonCreation(beanName);
            if (newSingleton) {
                //5、加入快取
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

這一步的流程就是:

  • (1)再次檢查快取是否已經載入過
  • (2)沒有載入,則記錄beanName 的載入狀態
  • (3)呼叫createBean()方法例項化 bean
  • (4)bean例項化完成之後,移除初始化狀態
  • (5)將例項化結果記錄到快取並刪除載入 bean 過程中所記錄到的各種輔助狀態

對於第(2)步和第(4)步,用來記錄 bean 的載入狀態,是用來對 迴圈依賴 進行檢測的。關鍵的方法在於第三步,呼叫了 ObjectFactory 的 getObject() 方法,實際回撥介面實現的是 createBean() 方法進行建立物件。

三、Bean的例項化階段:

(一)建立bean前的準備:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    RootBeanDefinition mbdToUse = mbd;
    // 確保此時bean類已經被解析,並且克隆 bean 定義,以防動態解析的類不能儲存在共享合併 bean 定義中。
    //1、鎖定 class,根據設定的 class 屬性或者根據 className 來解析 Class
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }
    // Prepare method overrides.
    //2、驗證及準備覆蓋的方法
    mbdToUse.prepareMethodOverrides();
    //3、解析指定bean,讓 beanPostProcessor 有機會返回代理而不是目標bean例項。
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
        // 短路操作,如果代理成功建立 bean 後,直接返回
        return bean;
    }
    
    //4、建立 bean
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    return beanInstance;
}

先來總結這個流程:

  • (1)根據設定的 class 屬性或者根據 className 來解析 Class
  • (2)驗證及準備覆蓋的方法 這個方法是用來處理以下兩個配置的:我們在解析預設標籤時,會識別 lookup-method 和 replaced-method 屬性,然後這兩個配置的載入將會統一存放在 beanDefinition 中的 methodOverrides 屬性裡。
  • (3)應用初始化前的後處理器,解析指定 bean 是否存在初始化前的短路操作
  • (4)建立 bean

1、處理 Override 屬性:

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    // Check that lookup methods exists.
    if (hasMethodOverrides()) {
        Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
        synchronized (overrides) {
            for (MethodOverride mo : overrides) {
                // 處理 override 屬性
                prepareMethodOverride(mo);
            }
        }
    }
}

可以看到,獲取類的過載方法列表,然後遍歷,一個一個進行處理。具體處理的是 lookup-method 和 replaced-method 屬性,這個步驟解析的配置將會存入 beanDefinition 中的 methodOverrides 屬性裡,是為了待會例項化做準備。 如果 bean 在例項化時,監測到 methodOverrides 屬性,會動態地位當前 bean 生成代理,使用對應的攔截器為 bean 做增強處理。

2、例項化前的前置處理:

// 讓 beanPostProcessor 有機會返回代理而不是目標bean例項。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
    // 短路操作,如果代理成功建立 bean 後,直接返回
    return bean;
}
 
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                // 執行前置攔截器的操作
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    // 執行後置攔截器的操作
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

在 doCreateBean() 方法前,有一個短路操作,如果後處理器成功,將會返回代理的 bean。

在 resolveBeforeInstantiation() 方法中,在確保 bean 資訊已經被解析完成,執行了兩個關鍵方法,從註釋中看到,一個是前置攔截器的操作,另一個就是後置攔截器的操作。

如果第一個前置攔截器例項化成功,就已經將單例 bean 放入快取中,它不會再經歷普通 bean 的建立過程,沒有機會進行後處理器的呼叫,所以在這裡的第二個步驟,就是為了這個 bean 也能應用後處理器的 postProcessAfterInitialization 方法。而如果這個bean沒有特定的前置處理,那說明這個bean是一個普通的bean,則按照下面的步驟進行建立

(二)bean的建立:doCreateBean()

到了這一步,就開始真正建立bean物件了,對應的原始碼就在doCreateBean()方法中,在這裡,我們先看一下bean的整體建立流程是怎麼樣的,然後再對其中的各個重要的方法作分析:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    //1、如果bean是單例,就先清除快取中的bean資訊
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        //2、根據指定bean使用對應的策略例項化bean,例如:工廠方法,建構函式自動注入,簡單初始化
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }
    //3、允許後處理處理器修改合併後的bean定義
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            mbd.postProcessed = true;
        }
    }
    //4、是否需要提前曝光,用來解決迴圈依賴時使用
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 第二個引數是回撥介面,實現的功能是將切面動態織入 bean
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    Object exposedObject = bean;
    //5、對 bean 進行填充,將各個屬性值注入
    // 如果存在對其它 bean 的依賴,將會遞迴初始化依賴的 bean
    populateBean(beanName, mbd, instanceWrapper);
    //6、呼叫初始化方法,例如 init-method
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    //7、迴圈依賴檢查
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        // earlySingletonReference 只有在檢測到有迴圈依賴的情況下才 不為空
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                // 如果 exposedObject 沒有在初始化方法中被改變,也就是沒有被增強
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    // 檢查依賴
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                // bean 建立後,它所依賴的 bean 一定是已經建立了
                // 在上面已經找到它有依賴的 bean,如果 actualDependentBeans 不為空
                // 表示還有依賴的 bean 沒有初始化完成,也就是存在迴圈依賴
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName);
            }
        }
    }
    // Register bean as disposable.
    // 8、根據 scope 註冊 bean
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
    return exposedObject;
}

這個doCreateBean()方法的主要流程可以總結為:

(1)如果要載入的bean是單例,就先清除快取中的bean資訊
(2)例項化 bean,將 BeanDifinition 轉化成 BeanWrapper
(3)後置處理器修改合併後的 bean 定義:bean 合併後的處理,Autowired 註解正式透過此方法實現諸如型別的預解析
(4)依賴處理,提前暴露bean的引用,主要用於解決迴圈依賴的問題
(5)屬性填充:將所有屬性填充到 bean 的例項中,如果存在對其他bean的依賴,將會遞迴初始化依賴的bean
(6)初始化bean:例如afeterPropertiesSet()和init-method屬性配置的方法
(7)迴圈依賴檢查
(8)註冊 DisposableBean:這一步是用來處理 destroy-method 屬性,在這一步註冊,以便在銷燬物件時呼叫。
(9)完成建立並返回。

1、createBeanInstance():例項化bean物件

在上面第(2)個步驟,做的是例項化bean,然後返回BeanWrapper。

createBeanInstance()這一步的流程主要透過兩種方式例項化bean物件,一種是工廠方法,另一種就是建構函式,將傳進來的 RootBeanDefinition 中的配置二選一生成 bean 例項,大致介紹功能:

  • 如果存在工廠方法則使用工廠方法進行初始化
  • 如果則使用有參構造行數例項化bean:一個類有多個建構函式,每個建構函式都有不同的引數,所以需要根據引數鎖定建構函式進行 bean 的例項化
  • 如果既不存在工廠方法,也不存在帶有引數的建構函式,會使用預設的建構函式進行 bean 的例項化
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    // Make sure bean class is actually resolved at this point.
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    // 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) {
            // 實際解析的是 org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
            // 建構函式自動注入
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            // 使用預設的建構函式
            return instantiateBean(beanName, mbd);
        }
    }
    // Candidate constructors for autowiring? 需要根據引數解析建構函式
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    // Preferred constructors for default construction?
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        // 建構函式注入
        return autowireConstructor(beanName, mbd, ctors, null);
    }
    // No special handling: simply use no-arg constructor. 沒有特殊的處理,使用預設建構函式構造
    return instantiateBean(beanName, mbd);
}

在這裡,我們簡單介紹下什麼是BeanWrapper:

  Spring中的Bean並不是以一個個的本來模樣存在的,由於Spring IOC容器中要管理多種型別的物件,因此為了統一對不同型別物件的訪問,Spring給所有建立的Bean例項穿上了一層外套,這個外套就是BeanWrapper,BeanWrapper實際上是對反射相關API的簡單封裝,使得上層使用反射完成相關的業務邏輯大大的簡化,我們要獲取某個物件的屬性,呼叫某個物件的方法,現在不需要在寫繁雜的反射API了以及處理一堆麻煩的異常,直接透過BeanWrapper就可以完成相關操作,簡直不要太爽了。

2、迴圈依賴:

  這裡對應前面的第(4)步的流程,提前暴露bean的引用,主要是用於解決迴圈依賴的問題

關鍵方法是 addSingletonFactory,作用是在 bean 開始初始化前將建立例項的 ObjectFactory 加入單例工廠,ObjectFactory 是建立物件時使用的工廠。在物件例項化時,會判斷自己依賴的物件是否已經建立好了,判斷的依據是檢視依賴物件的 ObjectFactory 是否在單例快取中,如果沒有建立將會先建立依賴的物件,然後將 ObjectFactory 放入單例快取中。

這時如果有迴圈依賴,需要提前對它進行暴露,讓依賴方找到並正常例項化。

有關迴圈依賴的內容可以閱讀這篇文章:https://blog.csdn.net/a745233700/article/details/110914620

3、populateBean():屬性注入

這裡對應前面的第(5)步,根據不同的注入型別進行屬性填充,然後呼叫後處理器進行處理,最終將屬性應用到 bean 中。主要流程如下:

(1)呼叫 InstantiationAwareBeanPostProcessor 處理器的 postProcessAfterInstantiation 方法,判斷控制程式是否繼續進行屬性填充

(2)根據注入型別(byType/byName),提取依賴的,統一存入 PropertyValues 中

(3)判斷是否需要進行 BeanPostProcessor 和 依賴檢查:

如果有後置處理器,將會應用 InstantiationAwareBeanPostProcessor 處理器的 postProcessProperties 方法,對屬性獲取完畢填充前,對屬性進行再次處理。
使用 checkDependencies 方法來進行依賴檢查
(4)將所有解析到的 PropertyValues  中的屬性填充至 BeanWrapper 中
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // 給 awareBeanPostProcessor 後處理器最後一次機會,在屬性設定之前修改bean的屬性
    boolean continueWithPropertyPopulation = true;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        ...
        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
            continueWithPropertyPopulation = false;
            break;
        }
        ...
    }
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            // 根據名字自動注入
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            // 根據型別自動注入
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }
    // 後處理器已經初始化
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // 需要依賴檢查
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
    PropertyDescriptor[] filteredPds = null;
    // 從 beanPostProcessors 物件中提取 BeanPostProcessor 結果集,遍歷後處理器
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        ...
    }
    // 在前面也出現過,用來進行依賴檢查
    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    checkDependencies(beanName, mbd, filteredPds, pvs);
    // 將屬性應用到 bean 中,使用深複製,將子類的屬性一併複製
    applyPropertyValues(beanName, mbd, bw, pvs);
}

4、initializeBean():初始化 bean

這裡對應前面的第(6)步,主要是用來進行我們設定的初始化方法的呼叫,不過在方法內部,還做了其它操作,首先我們先看下原始碼:

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    //1、處理Aware介面
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        // 如果沒有 securityManage,方法裡面校驗了 bean 的型別,需要引用 Aware 介面
        // 對特殊的 bean 處理:Aware/ BeanClassLoader / BeanFactoryAware
        invokeAwareMethods(beanName, bean);
    }
 
    //2、執行BeanPostProcessor前置處理:
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    //3、呼叫使用者自定義的 init-method 方法
    invokeInitMethods(beanName, wrappedBean, mbd);
 
    //4、執行BeanPostProcessor後置處理
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
 
    return wrappedBean;
}

這一步的流程中,主要做的處理操作有:

(1)處理Aware介面:

Spring會檢測該物件是否實現了xxxAware介面,透過Aware型別的介面,可以讓我們拿到Spring容器的一些相應的資源並注入:

  • ①如果這個Bean實現了BeanNameAware介面,會呼叫它實現的setBeanName(String beanId)方法,傳入Bean的名字;
  • ②如果這個Bean實現了BeanClassLoaderAware介面,呼叫setBeanClassLoader()方法,傳入ClassLoader物件的例項。
  • ②如果這個Bean實現了BeanFactoryAware介面,會呼叫它實現的setBeanFactory()方法,傳遞的是Spring工廠自身。
  • ③如果這個Bean實現了ApplicationContextAware介面,會呼叫setApplicationContext(ApplicationContext)方法,傳入Spring上下文;

(2)執行BeanPostProcessor前置處理:

如果想在Bean初始化前進行一些自定義的前置處理,那麼可以讓Bean實現了BeanPostProcessor介面,那將會呼叫postProcessBeforeInitialization(Object obj, String s)方法。

(3)呼叫自定義的 init 方法:

這一步就是找到使用者自定義的建構函式,然後呼叫它。

① 如果Bean實現了InitializingBean介面,執行afeterPropertiesSet()方法

②如果Bean在Spring配置檔案中配置了 init-method 屬性,則會自動呼叫其配置的初始化方法。

(4)執行BeanPostProcessor後置處理:

如果這個Bean實現了BeanPostProcessor介面,將會呼叫postProcessAfterInitialization(Object obj, String s)方法;由於這個方法是在Bean初始化結束時呼叫的,所以可以被應用於記憶體或快取技術;

在容器啟動階段我們講到BeanFactoryPostProcessor,這裡我們講到BeanPostProcessor,那麼BeanFactoryPostProcessor 和 BeanPostProcessor 有什麼區別呢?

  • BeanFactoryPostProcessor存在於容器啟動階段,而BeanPostProcessor存在於物件例項化階段,BeanFactoryPostProcessor關注物件被建立之前那些配置的修改,而BeanPostProcessor階段關注物件已經被建立之後的功能增強,替換等操作,這樣就很容易區分了。
  • BeanPostProcessor與BeanFactoryPostProcessor都是Spring在Bean生產過程中強有力的擴充套件點。Spring中著名的AOP(面向切面程式設計),其實就是依賴BeanPostProcessor對Bean物件功能增強的。

5、註冊 disposableBean:

這一步就是根據不同的 scope 進行 disposableBean 的註冊。同時也會註冊使用者自定義的銷燬邏輯,跟自定義的初始化邏輯一樣有兩種方式:(1)DisposableBean介面的destory();(2)destory-method引數配置的方法。

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
        // 單例模式
        if (mbd.isSingleton()) {
            // 註冊 DisposableBean
            registerDisposableBean(beanName,new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
        }
        else {
            // A bean with a custom scope...
            Scope scope = this.scopes.get(mbd.getScope());
            scope.registerDestructionCallback(beanName,new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
        }
    }
}

6、使用與銷燬:

經過了以上道道工序,Spring終於將Bean建立完成了,當Spring的Bean在為我們服務完之後,馬上就要消亡了(通常是在容器關閉的時候),這時候Spring將以回撥的方式呼叫我們自定義的銷燬邏輯,然後Bean就這樣走完了光榮的一生!

7、小結:

最後,我們再透過一張圖來一起看一看Bean例項化階段的執行順序是如何的:

參考文章:

https://juejin.cn/post/6844903871353978894#heading-5

https://juejin.cn/post/6929672218322731022#heading-0

版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

原文連結:https://blog.csdn.net/a745233700/article/details/113840727




相關文章