spring深入學習(十八) IOC 之開啟 bean 的例項化程序
在上篇部落格【死磕 Spring】—– 載入 bean 之 分析各 scope 的 bean 建立中有一個核心方法沒有講到 createBean()
,該方法的如下:
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 例項物件的引數
該抽象方法的預設實現是在類 AbstractAutowireCapableBeanFactory 中實現,如下:
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; // 確保此時的 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); } try { // 驗證和準備覆蓋方法 mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // 給 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 { // 執行真正建立 bean 的過程 Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
過程如下:
- 解析指定 BeanDefinition 的 class
- 處理 override 屬性
- 例項化的前置處理
- 建立 bean
解析指定 BeanDefinition 的 class
Class<?> resolvedClass = resolveBeanClass(mbd, beanName)
這個方法主要是解析 bean definition 的 class 類,並將已經解析的 Class 儲存在 bean definition 中以供後面使用。如果解析的 class 不為空,則會將該 BeanDefinition 進行克隆至 mbdToUse,這樣做的主要目的是以為動態解析的 class 是無法儲存到共享的 BeanDefinition 中。
處理 override 屬性
大家還記得 lookup-method 和 replace-method 這兩個配置功能?在部落格 【死磕 Spring】—– IOC 之解析Bean:解析 bean 標籤(三) 中已經詳細分析了這兩個標籤的用法和解析過程,知道解析過程其實就是講這兩個配置存放在 BeanDefinition 中的 methodOverrides 屬性中,我們知道在 bean 例項化的過程中如果檢測到存在 methodOverrides,則會動態地位為當前 bean 生成代理並使用對應的攔截器為 bean 做增強處理。具體的實現我們後續分析,現在先看 mbdToUse.prepareMethodOverrides()
都幹了些什麼事,如下:
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
if (hasMethodOverrides()) {
Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
synchronized (overrides) {
for (MethodOverride mo : overrides) {
prepareMethodOverride(mo);
}
}
}
}
如果存在 methodOverrides 則獲取所有的 override method ,然後通過迭代的方法一次呼叫 prepareMethodOverride()
,如下:
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) {
mo.setOverloaded(false);
}
}
根據方法名稱從 class 中獲取該方法名的個數,如果為 0 則丟擲異常,如果 為 1 則設定該過載方法沒有被過載。若一個類中存在多個過載方法,則在方法呼叫的時候還需要根據引數型別來判斷到底過載的是哪個方法。在設定過載的時候其實這裡做了一個小小優化,那就是當 count == 1
時,設定 overloaded = false
,這樣表示該方法沒有過載,這樣在後續呼叫的時候便可以直接找到方法而不需要進行方法引數的校驗。
誠然,其實 mbdToUse.prepareMethodOverrides()
並沒有做什麼實質性的工作,只是對 methodOverrides 屬性做了一些簡單的校驗而已。
例項化的前置處理
resolveBeforeInstantiation()
的作用是給 BeanPostProcessors 後置處理器返回一個代理物件的機會,其實在呼叫該方法之前 Spring 一直都沒有建立 bean ,那麼這裡返回一個 bean 的代理類有什麼作用呢?作用體現在後面的 if
判斷:
if (bean != null) {
return bean;
}
如果代理物件不為空,則直接返回代理物件,這一步驟有非常重要的作用,Spring 後續實現 AOP 就是基於這個地方判斷的。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
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 會單獨出博文來做詳細說明。
建立 bean
如果沒有代理物件,就只能走常規的路線進行 bean 的建立了,該過程有 doCreateBean()
實現,如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// BeanWrapper是對Bean的包裝,其介面中所定義的功能很簡單包括設定獲取被包裝的物件,獲取被包裝bean的屬性描述器
BeanWrapper instanceWrapper = null;
// 單例模型,則從未完成的 FactoryBean 快取中刪除
if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 使用合適的例項化策略來建立新的例項:工廠方法、建構函式自動注入、簡單初始化
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 包裝的例項物件
final Object bean = instanceWrapper.getWrappedInstance();
// 包裝的例項物件的型別
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 檢測是否有後置處理
// 如果有後置處理,則允許後置處理修改 BeanDefinition
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// applyMergedBeanDefinitionPostProcessors
// 後置處理修改 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;
}
}
// 解決單例模式的迴圈依賴
// 單例模式 & 執行迴圈依賴&當前單例 bean 是否正在被建立
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 提前將建立的 bean 例項加入到ectFactory 中
// 這裡是為了後期避免迴圈依賴
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
/*
* 開始初始化 bean 例項物件
*/
Object exposedObject = bean;
try {
// 對 bean 進行填充,將各個屬性值注入,其中,可能存在依賴於其他 bean 的屬性
// 則會遞迴初始依賴 bean
populateBean(beanName, mbd, instanceWrapper);
// 呼叫初始化方法
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);
}
}
/**
* 迴圈依賴處理
*/
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.");
}
}
}
}
try {
// 註冊 bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
整體的思路:
- 如果是單例模式,則清除 factoryBeanInstanceCache 快取,同時返回 BeanWrapper 例項物件,當然如果存在。
- 如果快取中沒有 BeanWrapper 或者不是單例模式,則呼叫
createBeanInstance()
例項化 bean,主要是將 BeanDefinition 轉換為 BeanWrapper
– MergedBeanDefinitionPostProcessor 的應用
– 單例模式的迴圈依賴處理
– 呼叫 populateBean()
進行屬性填充。將所有屬性填充至 bean 的例項中
– 呼叫 initializeBean()
初始化 bean
– 依賴檢查
– 註冊 DisposableBean
doCreateBean()
完成 bean 的建立和初始化工作,內容太多,這裡就只列出整體思路,下文開始將該方法進行拆分進行詳細講解,分佈從以下幾個方面進行闡述:
createBeanInstance()
例項化 beanpopulateBean()
屬性填充- 迴圈依賴的處理
initializeBean()
初始化 bean