bean 的載入(二)
之前文章主要對 getBean 方法進行簡單的介紹,和 FactoryBean 的作用,以及是如何從快取中獲取 bean。本文繼續講解 bean 的載入流程。
從 bean 的例項中獲取物件
在 getBean 方法裡,getObjectForBeanInstance()
是個常用的方法,無論是從快取中獲取 bean 還是根據不同 scope 策略來載入 bean。總而言之,我們在獲取到 bean 例項後第一步就是呼叫這個方法來檢測正確性,其實就是檢測當前 bean 是否為 FactoryBean 型別的 bean,如果是則呼叫 FactoryBean 例項的 getObject()作為返回值。
需要注意的是無論是快取中獲取到的 bean 還是透過 scope 策略載入的 bean 都是原始的 bean 狀態,我們需要的是工廠 bean 中定義 factory-method 方法中返回的 bean,而 getObjectForBeanInstance 方法就是完成這個工作的。
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//如果指定的name是工廠相關以"&"為字首,並且beanInstance又不是FactoryBean則校驗不透過
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
//現在我們有了這個bean例項,這個例項可能是FactoryBean,也可能是正常的bean
//如果是FactoryBean的話,我們使用它建立例項,但如果是使用者想要直接獲取工廠例項而不是工廠的getObject方法則需要在BeanName前加上"&"
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
//載入FactoryBean
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
//嘗試從快取中載入bean
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 執行到這裡表明beanInstance一定是一個FactoryBean
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// containsBeanDefinition檢測BeanDefinitionMap中也就是在所有一鍵載入的類中檢測是否定義beanName
if (mbd == null && containsBeanDefinition(beanName)) {
//將GenericBeanDefinition轉換為RootBeanDefinition,如果指定BeanName是子bean的話同時會合並父類的屬性
mbd = getMergedLocalBeanDefinition(beanName);
}
//是否是使用者定義的而不是應用程式本身定義的
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
我們先看一下 getObjectForBeanInstance 主要做了什麼:
- 對 FactoryBean 正確性進行驗證
- 對非 FactoryBean 不作任何處理
- 對 bean 進行轉換
- 將從 Factory 中解析 bean 的工作委託給了
getObjectFromFactoryBean
。
該段程式碼大多都是輔助程式碼,真正的核心程式碼委託給了getObjectFromFactoryBean
。
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
//重點方法 doGetObjectFromFactoryBean
object = doGetObjectFromFactoryBean(factory, beanName);
//...省略
}
else {
//重點方法 doGetObjectFromFactoryBean
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
這段程式碼就是返回的 bean 如果是單例的則需要保證全域性唯一,也正因為是單例的所以不必重複建立,可以用快取來提高效能。
我們進入doGetObjectFromFactoryBean
方法中。
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
//是否需要許可權校驗
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//直接呼叫getObject方法(重點)
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}
在該方法中,我們終於看到了想要看到的程式碼,也就是object = factory.getObject()
。之前我們已經講過了 FactoryBean 的呼叫方法,如果 bean 是 FactoryBean 型別,則當提取 bean 時提取的並不是 factoryBean 而是 factoryBean 的 getObject 方法的返回值。
獲取單例
之前我們說過如果快取中不存在已經載入的 bean 則需要重頭開始 bean 的載入,在 Spring 中使用 getSingleton 的過載方法實現了 bean 的載入過程。
getBean 方法:
// 例項化依賴的bean後就可以例項化mbd本身了
// 如果BeanDefinition為單例
if (mbd.isSingleton()) {
//建立Bean例項物件,並且註冊給所依賴的物件
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//從單例快取中刪除bean例項
//因為單例模式下為了解決迴圈依賴,可能它已經存在了,所以將其銷燬
destroySingleton(beanName);
throw ex;
}
});
//如果是普通bean,直接返回,如果是FactoryBean,則返回它的getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
進入 getSingleton 方法:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
//加鎖
synchronized (this.singletonObjects) {
//首先檢查對應的bean是否已經載入過,
Object singletonObject = this.singletonObjects.get(beanName);
//如果為空則需要進行singleton的bean初始化
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
//程式碼(1)
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 透過回撥方式獲取bean例項。
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//程式碼(2)
afterSingletonCreation(beanName);
}
if (newSingleton) {
//加入快取 程式碼(3)
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
上述程式碼使用了回撥方法,在單例建立的前後做了些準備及處理操作,而真正獲取單例 bean 的方法是在 ObjectFactory 型別的例項 singletonFactory 中。我們先看一下 getSingleton 方法主要做了什麼:
- 檢查快取是否已經載入過
- 若沒有載入,則記錄 beanName 為正在載入狀態
- 載入單例前記錄載入狀態,程式碼(1)
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
這個方法主要做的就是記錄載入狀態,this.singletonsCurrentlyInCreation.add(beanName)
將當前正要建立的 bean 記錄在快取中,這樣就可以對迴圈依賴進行檢測。
- 獲取 bean 例項
- 呼叫載入單例後的處理方法,程式碼(2)
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
和上述類似,只是這裡是從快取中移除 bean 的正在載入狀態。
- 將 bean 加入快取,並刪除載入 bean 過程中所記錄的各種輔助狀態。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
- 返回處理結果
現在我們已經瞭解了 bean 的邏輯架構,但是 bean 的載入邏輯是在傳入 ObjectFactory 型別的引數 singletonFactory 中定義的。
//建立Bean例項物件,並且註冊給所依賴的物件
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//從單例快取中刪除bean例項
//因為單例模式下為了解決迴圈依賴,可能它已經存在了,所以將其銷燬
destroySingleton(beanName);
throw ex;
}
});
我們進入 createBean 方法中繼續檢視。
準備建立 bean
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.
// 鎖定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 {
//程式碼(1)
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);
}
}
- 根據設定的 class 屬性或者根據 className 來解析 Class
- 對 override 屬性進行標記及驗證
我們知道在 Spring 的配置中並沒有類似於 override-methdo 之類的配置,那麼該方法的作用是什麼?
我們之前說過 Spring 配置中存在 lookup-method 和 replace-method 的,這兩個配置的載入就是將配置統一存放在 BeanDefinition 中的 methodOverrides 屬性中,而這個函式其實也就是針對這兩個配置的。
- 應用初始化前的後處理器,解析指定 bean 是否存在初始化前的短路操作
- 建立 bean
我們先看一下 override 屬性標記及驗證的邏輯實現
處理 override 屬性
進入 prepareMethodOverrides 方法:
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exist and determine their overloaded status.
if (hasMethodOverrides()) {
getMethodOverrides().getOverrides().forEach(this::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) {
// Mark override as not overloaded, to avoid the overhead of arg type checking.
// 標記MethodOverride暫未被覆蓋,避免引數型別檢查的開銷
mo.setOverloaded(false);
}
}
剛才說到 lookup-method 和 replace-method 兩個配置功能是統一存放在 BeanDefinition 中的 methodOverrides 屬性中,這兩個功能實現原理就是在 bean 例項化的時候如果檢測到存在 methodOverrides 屬性,就會動態地為當前 bean 生成代理並使用對應的攔截器對 bean 做增強處理。
需要提到的是,對於方法的匹配來說,如果一個類中有多個過載方法則需要根據引數型別進行匹配。如果類中只有方法只有一個那麼就設定該方法沒有被過載,這樣在後續時候可以直接使用找到的方法,不需要進行方法的引數匹配驗證,而且還可以提前對方法存在性進行驗證,所謂一箭雙鵰。
例項化的前置處理
在呼叫 doCreateBean 之前,還使用了 resolveBeforeInstantiation(beanName, mbdToUse)方法對 BeanDefinition 中的屬性做些前置處理。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
該段程式碼的重點就是這個 if 條件,當處理結果不為 null 的時候就會跳過後續 bean 的建立直接返回結果。我們熟知的 AOP 功能就是基於這裡判斷的。
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) {
//程式碼(1)
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
//程式碼(2)
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
該方法中的重點就是這兩個方法:applyBeanPostProcessorsBeforeInstantiation、applyBeanPostProcessorsAfterInitialization。這兩個方法實現都很簡單,就是對後處理器中所有的 InstantiationAwareBeanPostProcessor 型別的後處理器進行 postProcessBeforeInstantiation 方法和 BeanPostProcessor 的 postProcessAfterInitialization 方法的呼叫。
1. 例項化前的後處理器應用
bean 的例項化前呼叫,也就是將 AbstractBeanDefinition 轉換為 BeanWrapper 前的處理。給子類一個修改 BeanDefinition 的機會,也就是呼叫這個方法後 bean 就有可能產生變化,有可能是經過處理的代理 bean,也可能是 cglib 生成的。後續會詳細介紹,現在只需要明白在 bean 例項化前會呼叫後處理器的方法進行處理。
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. 例項化後的後處理器應用
Spring 的規則是在 bean 初始化後儘可能保證將註冊的後處理器 postProcessAfterInitialization 方法應用到 bean 中,如果返回的 bean 不為空,則不需要再經歷 bean 的建立過程。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}