本文結合《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; }