Spring源碼分析(十六)准備創建bean


本文結合《Spring源碼深度解析》來分析Spring 5.0.6版本的源代碼。若有描述錯誤之處,歡迎指正。

 

我們不可能指望在一個函數中完成一個復雜的邏輯,而且我們跟蹤了這么多Spring代碼, 經歷了這么多函數,或多或少也發現了一些規律:一個真正干活的函數其實是以do開頭的, 比如 doGetObjectFromFactoryBean;而給我們錯覺的函數,比如 getObjectFromFactoryBean,其 實只是從全局角度去做些統籌的工作。這個規則對於 createBean也不例外,那么讓我們看看在 createBean函數中做了哪些准備工作。

/**
 * Central method of this class: creates a bean instance,
 * populates the bean instance, applies post-processors, etc.
 * @see #doCreateBean
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    if (logger.isDebugEnabled()) {
        logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;

    // Make sure bean class is actually resolved at this point, and
    // clone the bean definition in case of a dynamically resolved Class
    // which cannot be stored in the shared merged bean definition.
    // 鎖定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.
    // 驗證及准備覆蓋的方法
    try {
        mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                beanName, "Validation of method overrides failed", ex);
    }

    try {
        // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        // 給BeanPostProcessors一個機會返回代理來替代真正的實例
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
    }

    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        // A previously detected exception with proper bean creation context already,
        // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
}

從代碼中我們可以總結出函數完成的具體步驟及功能。

(1)根據設置的class屬性或者根據dassName來解析Class。

(2)對override屬性進行標記及驗證。

很多讀者可能會不知道這個方法的作用,因為在Spring的配置里面根本就沒有諸如 override-method之類的配置,那么這個方法到底是干什么用的呢?

其實在Spring中確實沒有override-method這樣的配置,但是如果讀過前面的部分,可能會有所發現,在Spring配置中是存在lookup~method和replace-method的,而這兩個配置的加載其實就是將配置統一存放在BeanDefinition中的methodOverrides屬性里,而這個函數的操作其實也就是針對於這兩個配置的。

(3)應用初始化前的后處理器,解析指定bean是否存在初始化前的短路操作。

(4)創建 bean。

我們首先査看下對override屬性標記及驗證的邏輯實現。

一、處理override屬性

查看源碼中 AbstractBeanDeflnition 類的 prepareMethodOverrides 方法:

/**
 * Validate and prepare the method overrides defined for this bean.
 * Checks for existence of a method with the specified name.
 * @throws BeanDefinitionValidationException in case of validation failure
 */
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    // Check that lookup methods exists.
    if (hasMethodOverrides()) {
        Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
        synchronized (overrides) {
            for (MethodOverride mo : overrides) {
                prepareMethodOverride(mo);
            }
        }
    }
}

/**
 * Validate and prepare the given method override.
 * Checks for existence of a method with the specified name,
 * marking it as not overloaded if none found.
 * @param mo the MethodOverride object to validate
 * @throws BeanDefinitionValidationException in case of validation failure
 */
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
    // 獲取對應類中對應方法名的個數
    int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
    if (count == 0) {
        throw new BeanDefinitionValidationException(
                "Invalid method override: no method with name '" + mo.getMethodName() +
                "' on class [" + getBeanClassName() + "]");
    }
    else if (count == 1) {
        // Mark override as not overloaded, to avoid the overhead of arg type checking.
        // 標記MethodOverride暫未被覆蓋,避免參數類型檢查的開銷
        mo.setOverloaded(false);
    }
}

通過以上兩個函數的代碼你能體會到它所要實現的功能嗎?之前反復提到過,在Spring配置中存在lookup-method和replace-method兩個配置功能,而這兩個配置的加載其實就是將配置統一存放在BeanDefmition中的methodOverrides屬性里,這兩個功能實現原理其實是在bean實例化的時候如果檢測到存在methodOverrides屬性,會動態地為當前bean生成代理並使用對應的攔截器為bean做增強處理,相關邏輯實現在bean的實例化部分詳細介紹。

但是,這里要提到的是,對於方法的匹配來講,如果一個類中存在若干個重載方法,那么,在函數調用及增強的時候還需要根據參數類型進行匹配,來最終確認當前調用的到底是哪個函數。但是,Spring將一部分匹配工作在這里完成了,如果當前類中的方法只有一個,那么就設置該方法沒有被重載,這樣在后續調用的時候便可以直接使用找到的方法,而不需要進行方法的參數匹配驗證了,而且還可以提前對方法存在性進行驗證,正可謂一箭雙雕。

二、實例化的前置處理

在真正調用doCreate方法創建bean的實例前使用了這樣一個方法resolveBeforelnstantiation (bcanName, mbd)對BeanDefmigiton中的屬性做些前置處理。當然,無論其中是否有相應的邏輯實現我們都可以理解,因為真正邏輯實現前后留有處理函數也是可擴展的一種體現,但是, 這並不是最重要的,在函數中還提供了一個短路判斷,這才是最為關鍵的部分。

if (bean != null) {
    return bean;
}

當經過前置處理后返回的結果如果不為空,那么會直接略過后續的Bean的創建而直接返回結果。這一特性雖然很容易被忽略,但是卻起着至關重要的作用,我們熟知的AOP功能就是基於這里的判斷的。

/**
 * Apply before-instantiation post-processors, resolving whether there is a
 * before-instantiation shortcut for the specified bean.
 * @param beanName the name of the bean
 * @param mbd the bean definition for the bean
 * @return the shortcut-determined bean instance, or {@code null} if none
 */
@Nullable
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;
}

此方法中最吸引我們的無疑是兩個方法applyBeanPostProcessorsBeforelnstantiation以及 applyBeanPostProcessorsAfterlnitialization。兩個方法實現的非常簡單,無非是對后處理器中的所有InstantiationAwareBeanPostProcessor 類型的后處理器進行 postProcessBeforelnstantiation 方 法和 BeanPostProcessor 的 postProcessAfterlnitialization 方法的調用。

1. 實例化前的后處理器應用

bean的實例化前調用,也就是將AbsractBeanDefinition轉換為BeanWrapper前的處理。給子類一個修改BeanDefinition的機會,也就是說當程序經過這個方法后,bean可能已經不是我們認為的bean了,而是或許成為了一個經過處理的代理bean,可能是通過cglib生成的,也可能是通過其它技術生成的。這在第7章中會詳細介紹,我們只需要知道,在bean的實例化前會調用后處理器的方法進行處理。

/**
 * Apply InstantiationAwareBeanPostProcessors to the specified bean definition
 * (by class and name), invoking their {@code postProcessBeforeInstantiation} methods.
 * <p>Any returned object will be used as the bean instead of actually instantiating
 * the target bean. A {@code null} return value from the post-processor will
 * result in the target bean being instantiated.
 * @param beanClass the class of the bean to be instantiated
 * @param beanName the name of the bean
 * @return the bean object to use instead of a default instance of the target bean, or {@code null}
 * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
 */
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
            if (result != null) {
                return result;
            }
        }
    }
    return null;
}

2. 實例化后的后處理器應用

在講解從緩存中獲取單例bean的時候就提到過,Spring中的規則是在bean的初始化后盡可能保證將注冊的后處理器的postProcessAfterInitialization方法應用到該bean中,因為如果返回的bean不為空,那么便不會再次經歷普通bean的創建過程,所以只能在這里應用后處理器的postProcessAfterlnitialization方法。

@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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM