一.簡介
對於實例化好的單例bean,getBean(String)方法並不會再一次去創建,而是從緩存中獲取。如果某個bean還未實例化,這個時候就無法命中緩存,此時,就要根據bean的配置信息去創建這個bean了。相較於getBean(String)方法的實現邏輯,創建bean的方法create(String,RootBeanDefinition,Object[])及所調用的方法邏輯更加複雜一些,本次就先從大體上分析createBean方法邏輯。
二.源碼分析
2.1 創建bean實例的入口
我們首先要知道createBean方法在哪些被調用,如下:
public T doGetBean(...) {
// 省略不相關代碼
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 省略不相關代碼
}
上面是doGetBean方法的代碼片段,從中可以發現調用了createBean方法。createBean方法被匿名工廠類的getObject方法包裹,但是這個匿名工廠類並未直接調用getObject方法,而是將自身作爲參數傳遞給getSingleton(String,ObjectFactory)方法。getSingleton這個方法實現我們之前講過,邏輯如下:
1.先從singletonObjects集合獲取bean實例,若不爲空,直接返回
2.若爲空,進入創建bean實例階段。先將beanName添加到singletonCurrentlyInCreation
3.通過getObject方法調用createBean方法創建bean實例
4.將beanName從singletonCurrentlyInCreation集合中移除
5.將<beanName,singletonObject>映射緩存到singletonObjects集合中
從上面的分析中,瞭解可createBean在什麼時候和地方被調用,下來需要了解createBean做了什麼事情。
2.2 createBean
源碼分析如下:
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 解析 bean 的類型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
try {
// 處理 lookup-method 和 replace-method 配置,Spring 將這兩個配置統稱爲 override method
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 在 bean 初始化前應用後置處理,如果後置處理返回的 bean 不爲空,則直接返回
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);
}
// 調用 doCreateBean 創建 bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
執行流程如下:
1.根據設置的class屬性或者根據className來解析class
2.對override屬性進行標記及驗證。在Spring配置中是存着lookup-method和replace-method的,而這兩個配置的加載就是講配置同一存放在BeanDefinition中的methodOverrides屬性裏,而這個函數的操作其實也就是針對這兩個配置的。
3.應用初始化前的後處理器,解析指定bean是否存在初始化的短路操作。
4.創建bean。
2.3 驗證和準備override方法
在Spring配置中是存着lookup-method和replace-method的,而這兩個配置的加載就是講配置同一存放在BeanDefinition中的methodOverrides屬性裏,這兩個功能實現原理其實是在bean實例化的時候如果檢測到methodOverrides屬性裏,會動態地爲當前bean生成代理並使用對應的攔截器對bean做增強處理,相關邏輯實現在bean實例化會講到。
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
MethodOverrides methodOverrides = getMethodOverrides();
if (!methodOverrides.isEmpty()) {
Set<MethodOverride> overrides = methodOverrides.getOverrides();
synchronized (overrides) {
// 循環處理每個 MethodOverride 對象
for (MethodOverride mo : overrides) {
prepareMethodOverride(mo);
}
}
}
}
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
// 獲取方法名爲 mo.getMethodName() 的方法數量,當方法重載時,count 的值就會大於1
int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
// count = 0,表明根據方法名未找到相應的方法,此時拋出異常
if (count == 0) {
throw new BeanDefinitionValidationException(
"Invalid method override: no method with name '" + mo.getMethodName() +
"' on class [" + getBeanClassName() + "]");
}
// 若 count = 1,表明僅存在已方法名爲 mo.getMethodName(),這意味着方法不存在重載
else if (count == 1) {
// 方法不存在重載,則將 overloaded 成員變量設爲 false
mo.setOverloaded(false);
}
}
上面的源碼中,prepareMethodOverrides方法循環調用了prepareMethodOverride 方法,並沒其他的太多邏輯。主要準備工作都是在 prepareMethodOverride 方法中進行的。prepareMethodOverride 這個方法主要用於獲取指定方法的方法數量 count,並根據 count 的值進行相應的處理。count = 0 時,表明方法不存在,此時拋出異常。count = 1 時,設置 MethodOverride 對象的overloaded成員變量爲 false。這樣做的目的在於,提前標註名稱mo.getMethodName的方法不存在重載,在使用 CGLIB 增強階段就不需要進行校驗,直接找到某個方法進行增強即可。
2.4 實例化的前置處理
用戶通過實現 BeanPostProcessor 接口,並將實現類配置到 Spring 的配置文件中(或者使用註解),即可在 bean 初始化前後進行自定義操作。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
// 檢測是否解析過,mbd.beforeInstantiationResolved 的值在下面的代碼中會被設置
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
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
2.4.1 實例化前的後處理器應用
bean的實例化前調用,也就是將AbstractBeanDefinition轉換爲BeanWrapper前的處理。給子類一個修改BeanDefinition機會,也就是說當程序經過這個方法後,bean可能不是我們認爲的bean了,而是或許稱爲了一個經過處理代理的bean,也可能是通過cglib生成的也可能是通過其他技術生成的。
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// InstantiationAwareBeanPostProcessor 一般在 Spring 框架內部使用,不建議用戶直接使用
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// bean 初始化前置處理
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
2.4.2 實例化後的後處理器應用
Spring中的規則是在bean的初始化後儘可能保證將註冊的後處理器的postProcessAfterInitialization方法應用到該bean中,因爲如果返回的bean不爲空,那麼便不會再次經歷普通bean的創建過程,所以只能在這裏應用後置處理器postProcessAfterInitialization。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
// bean 初始化後置處理
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
2.5 調用 doCreateBean 方法創建 bean
當經歷過後resolveBeforeInstantiation方法之後,程序有兩個選擇,如果創建了代理或者重寫了InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法並在方法的postBeforeInstantiation中改變了bean,則直接返回就行,否則就要驚醒常規bean的創建,常規bean的創建就是doCreateBean中完成的。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
/*
* BeanWrapper 是一個基礎接口,由接口名可看出這個接口的實現類用於包裹 bean 實例。
* 通過 BeanWrapper 的實現類可以方便的設置/獲取 bean 實例的屬性
*/
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 從緩存中獲取 BeanWrapper,並清理相關記錄
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/*
* 創建 bean 實例,並將實例包裹在 BeanWrapper 實現類對象中返回。createBeanInstance
* 中包含三種創建 bean 實例的方式:
* 1. 通過工廠方法創建 bean 實例
* 2. 通過構造方法自動注入(autowire by constructor)的方式創建 bean 實例
* 3. 通過無參構造方法方法創建 bean 實例
*
* 若 bean 的配置信息中配置了 lookup-method 和 replace-method,則會使用 CGLIB
* 增強 bean 實例。後面會解析到
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 此處的 bean 可以認爲是一個原始的 bean 實例,暫未填充屬性
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// 這裏又遇到後置處理了,此處的後置處理是用於處理已“合併的 BeanDefinition”。
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
/*
* earlySingletonExposure 是一個重要的變量,這裏要說明一下。該變量用於表示是否提前暴露
* 單例 bean,用於解決循環依賴。earlySingletonExposure 由三個條件綜合而成,如下:
* 條件1:mbd.isSingleton() - 表示 bean 是否是單例類型
* 條件2:allowCircularReferences - 是否允許循環依賴
* 條件3:isSingletonCurrentlyInCreation(beanName) - 當前 bean 是否處於創建的狀態中
*
* earlySingletonExposure = 條件1 && 條件2 && 條件3
* = 單例 && 是否允許循環依賴 && 是否存於創建狀態中。
*/
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");
}
// 添加工廠對象到 singletonFactories 緩存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
// 獲取早期 bean 的引用,如果 bean 中的方法被 AOP 切點所匹配到,此時 AOP 相關邏輯會介入
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
Object exposedObject = bean;
try {
// 向 bean 實例中填充屬性,populateBean 方法也是一個很重要的方法,後面會專門寫文章分析
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
/*
* 進行餘下的初始化工作,詳細如下:
* 1. 判斷 bean 是否實現了 BeanNameAware、BeanFactoryAware、
* BeanClassLoaderAware 等接口,並執行接口方法
* 2. 應用 bean 初始化前置操作
* 3. 如果 bean 實現了 InitializingBean 接口,則執行 afterPropertiesSet
* 方法。如果用戶配置了 init-method,則調用相關方法執行自定義初始化邏輯
* 4. 應用 bean 初始化後置操作
*
* 另外,AOP 相關邏輯也會在該方法中織入切面邏輯,此時的 exposedObject 就變成了
* 一個代理對象了
*/
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) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 若 initializeBean 方法未改變 exposedObject 的引用,則此處的條件爲 true。
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(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 {
// 註冊銷燬邏輯
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
方法的執行流程如下:
1.如果是單例則需要清理緩存
2.實例化bean,將BeanDefinition轉換爲BeanWrapper。這裏的轉換有:
- 如果存在工廠方法則使用工廠方法進行初始化
- 一個類有多個構造函數,每個構造函數都有不同的參數,所以需要參數鎖定構造函數並進行初始化
- 如果既不存在工廠方法也不存在帶有參數的構造函數,則使用默認的構造函數進行bean的實例化
3.應用 MergedBeanDefinitionPostProcessor 後置處理器相關邏輯
4.根據條件決定是否提前暴露 bean 的早期引用(early reference),用於處理循環依賴問題
5.屬性填充,將所有屬性填充到bean的實例中
6.循環依賴檢查,在這個步驟裏會檢測已經加載的bean是否已經出現了依賴循環,並判斷是否需要拋出異常。
7.註冊DisposableBean,以便於銷燬時候調用
8.完成創建並返回
參考:Spring IOC 容器源碼分析 - 創建單例 bean 的過程
《Spring源碼深度解析》