spring-core IoC 之加載 Bean:建立 Bean主流程
- 1. createBean 抽象方法
- 2. createBean 預設實作
-
- 2.1 解析指定 BeanDefinition 的 class
- 2.2 處理 override 屬性
- 2.3 執行個體化的前置處理
- 2.4 建立 Bean
- 3. 小結
1. createBean 抽象方法
有一個核心方法沒有講到, #createBean(String beanName, RootBeanDefinition mbd, Object[] args) 方法,代碼如下:
// AbstractBeanFactory.java
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException;
- 該方法定義在 AbstractBeanFactory 中,其含義是根據給定的 BeanDefinition 和 args 執行個體化一個 Bean 對象。
- 如果該 BeanDefinition 存在父類,則該 BeanDefinition 已經合并了父類的屬性。
- 所有 Bean 執行個體的建立,都會委托給該方法實作。
-
該方法接受三個方法參數:
beanName :bean 的名字。
mbd :已經合并了父類屬性的(如果有的話)BeanDefinition 對象。
args :用于構造函數或者工廠方法建立 Bean 執行個體對象的參數
2. createBean 預設實作
該抽象方法的預設實作是在類 AbstractAutowireCapableBeanFactory 中實作,代碼如下:
// AbstractAutowireCapableBeanFactory.java
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("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.
// <1> 確定此時的 bean 已經被解析了
// 如果擷取的class 屬性不為null,則克隆該 BeanDefinition
// 主要是因為該動态解析的 class 無法儲存到到共享的 BeanDefinition
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
// <2> 驗證和準備覆寫方法
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.
// <3> 執行個體化的前置處理
// 給 BeanPostProcessors 一個機會用來傳回一個代理類而不是真正的類執行個體
// AOP 的功能就是基于這個地方
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 {
// <4> 建立 Bean 對象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("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> 處,解析指定 BeanDefinition 的 class 屬性。
<2> 處,處理 override 屬性。
<3> 處,執行個體化的前置處理。
<4> 處,建立 Bean 對象。
2.1 解析指定 BeanDefinition 的 class
// AbstractAutowireCapableBeanFactory.java
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
- #resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>… typesToMatch) 方法,主要是解析 bean definition 的 class 類,并将已經解析的 Class 存儲在 bean definition 中以供後面使用。
- 如果解析的 class 不為空,則會将該 BeanDefinition 進行設定到 mbdToUse 中。這樣做的主要目的是,以為動态解析的 class 是無法儲存到共享的 BeanDefinition 中。
2.2 處理 override 屬性
大家還記得 lookup-method 和 replace-method 這兩個配置功能?在部落格 《【死磕 Spring】—— IoC 之解析 标簽:meta、lookup-method、replace-method》 中,已經詳細分析了這兩個标簽的用法和解析過程,知道解析過程其實就是講這兩個配置存放在 BeanDefinition 中的 methodOverrides 屬性中。
我們知道在 bean 執行個體化的過程中如果檢測到存在 methodOverrides ,則會動态地位為目前 bean 生成代理并使用對應的攔截器為 bean 做增強處理。具體的實作我們後續分析,現在先看 mbdToUse.prepareMethodOverrides() 代碼塊,都幹了些什麼事,代碼如下:
// AbstractBeanDefinition.java
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exists.
if (hasMethodOverrides()) {
Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
synchronized (overrides) { // 同步
// 循環,執行 prepareMethodOverride
for (MethodOverride mo : overrides) {
prepareMethodOverride(mo);
}
}
}
}
如果存在 methodOverrides ,則擷取所有的 override method ,然後通過疊代的方法一次調用 #prepareMethodOverride(MethodOverride mo) 方法。代碼如下:
// AbstractBeanDefinition.java
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.
mo.setOverloaded(false);
}
}
- 根據方法名稱,從 class 中擷取該方法名的個數:
- 如果個數為 0 ,則抛出 BeanDefinitionValidationException 異常。
- 如果個數為 1 ,則設定該重載方法沒有被重載。
- 若一個類中存在多個重載方法,則在方法調用的時候還需要根據參數類型來判斷到底重載的是哪個方法。在設定重載的時候其實這裡做了一個小小優化,那就是當 count == 1 時,設定 overloaded = false ,這樣表示該方法沒有重載。這樣,在後續調用的時候,便可以直接找到方法而不需要進行方法參數的校驗。
誠然,其實 mbdToUse.prepareMethodOverrides() 代碼塊,并沒有做什麼實質性的工作,隻是對 methodOverrides 屬性做了一些簡單的校驗而已。
2.3 執行個體化的前置處理
#resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) 方法的作用,是給 BeanPostProcessors 後置處理器傳回一個代理對象的機會。其,實在調用該方法之前 Spring 一直都沒有建立 bean ,那麼這裡傳回一個 bean 的代理類有什麼作用呢?作用展現在後面的 if 判斷,代碼如下:
// AbstractBeanDefinition.java
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// ↓↓↓
if (bean != null) {
return bean;
}
- 如果代理對象不為空,則直接傳回代理對象,這一步驟有非常重要的作用,Spring 後續實作 AOP 就是基于這個地方判斷的。
- #resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) 方法,代碼如下:
// AbstractAutowireCapableBeanFactory.java
@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;
}
這個方法核心就在于 applyBeanPostProcessorsBeforeInstantiation() 和 applyBeanPostProcessorsAfterInitialization() 兩個方法,before 為執行個體化前的後處理器應用,after 為執行個體化後的後處理器應用。
由于本文的主題是建立 bean ,關于 Bean 的增強處理後續 LZ 會單獨出博文來做詳細說明。
2.4 建立 Bean
如果沒有代理對象,就隻能走正常的路線進行 bean 的建立了,該過程有 #doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) 方法來實作。代碼如下:
// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
// BeanWrapper 是對 Bean 的包裝,其接口中所定義的功能很簡單包括設定擷取被包裝的對象,擷取被包裝 bean 的屬性描述器
BeanWrapper instanceWrapper = null;
// <1> 單例模型,則從未完成的 FactoryBean 緩存中删除
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// <2> 使用合适的執行個體化政策來建立新的執行個體:工廠方法、構造函數自動注入、簡單初始化
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 包裝的執行個體對象
final Object bean = instanceWrapper.getWrappedInstance();
// 包裝的執行個體對象的類型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// <3> 判斷是否有後置處理
// 如果有後置處理,則允許後置處理修改 BeanDefinition
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 後置處理修改 BeanDefinition
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// <4> 解決單例模式的循環依賴
boolean earlySingletonExposure = (mbd.isSingleton() // 單例模式
&& this.allowCircularReferences // 運作循環依賴
&& isSingletonCurrentlyInCreation(beanName)); // 目前單例 bean 是否正在被建立
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 提前将建立的 bean 執行個體加入到 singletonFactories 中
// 這裡是為了後期避免循環依賴
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
// 開始初始化 bean 執行個體對象
Object exposedObject = bean;
try {
// <5> 對 bean 進行填充,将各個屬性值注入,其中,可能存在依賴于其他 bean 的屬性
// 則會遞歸初始依賴 bean
populateBean(beanName, mbd, instanceWrapper);
// <6> 調用初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// <7> 循環依賴處理
if (earlySingletonExposure) {
// 擷取 earlySingletonReference
Object earlySingletonReference = getSingleton(beanName, false);
// 隻有在存在循環依賴的情況下,earlySingletonReference 才不會為空
if (earlySingletonReference != null) {
// 如果 exposedObject 沒有在初始化方法中被改變,也就是沒有被增強
if (exposedObject == bean) {
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);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
// <8> 注冊 bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
} catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
整體的思路:
<1> 處,如果是單例模式,則清除緩存。
詳細解析,見 TODO
<2> 處,調用 #createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) 方法,執行個體化 bean ,主要是将 BeanDefinition 轉換為 org.springframework.beans.BeanWrapper 對象。
詳細解析,見 《【死磕 Spring】—— IoC 之加載 Bean:建立 Bean(二)》 和 《【死磕 Spring】—— IoC 之加載 Bean:建立 Bean(三)》 中。
<3> 處,MergedBeanDefinitionPostProcessor 的應用。
詳細解析,見 TODO
<4> 處,單例模式的循環依賴處理。
詳細解析,見 《【死磕 Spring】—— IoC 之加載 Bean:建立 Bean(五)之循環依賴處理》 。
<5> 處,調用 #populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) 方法,進行屬性填充。将所有屬性填充至 bean 的執行個體中。
詳細解析,見 《【死磕 Spring】—— IoC 之加載 bean:建立 Bean(四)之屬性填充》 。
<6> 處,調用 #initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) 方法,初始化 bean 。
詳細解析,見 《死磕 Spring】—— IoC 之加載 Bean:建立 Bean(六)之初始化 Bean 對象》 。
<7> 處,依賴檢查。
詳細解析,見 TODO
<8> 處,注冊 DisposableBean 。
詳細解析,見 TODO
3. 小結
#doCreateBean(…) 方法,完成 bean 的建立和初始化工作,内容太多,這裡就隻列出整體思路。下文開始,将該方法進行拆分進行詳細講解,分别從以下幾個方面進行闡述:
- #createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) 方法,執行個體化 bean 。
- 循環依賴的處理。
- #populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) 方法,進行屬性填充。
- #initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) 方法,初始化 Bean 。