解析完配置後,來看下 bean 是如何加載的java
既然咱們 Spring
辛辛苦苦將 bean
進行了註冊,固然須要拿出來進行使用,在使用以前還須要通過一個步驟,就是 bean
的加載。git
在第一篇筆記提到了,完成 bean
註冊到 beanDefinitionMap
註冊表後,還調用了不少後處理器的方法,其中有一個方法 finishBeanFactoryInitialization()
,註釋上面寫着 Instantiate all remaining (non-lazy-init) singletons
,意味着非延遲加載的類,將在這一步實例化,完成類的加載。github
而咱們使用到 context.getBean("beanName")
方法,若是對應的 bean
是非延遲加載的,那麼直接就能拿出來進行使用,而延遲加載的 bean
就須要上面的步驟進行類的加載,加載完以後才能進行使用~spring
下面一塊兒來看下這兩個步驟中, bean
是如何進行加載的。緩存
咱們的代碼分析都是圍繞着這個方法,請同窗們提早定位好位置:bash
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean多線程
這個 bean
加載的代碼量是有點多的,已經超過 100 行,因此整理了時序圖,但願能對加載流程有個清晰的概覽:併發
這個時序圖介紹了 bean
加載的大致流程,還有不少細節沒在圖中進行展現。咱們先對總體流程有個瞭解,而後跟着代碼一塊兒深刻分析吧。mvc
再提示一下:因爲代碼量不少,每次貼大段代碼看起來會比較吃力,因此展現的是我認爲的關鍵代碼,下載項目看完整註釋,跟着源碼一塊兒分析~app
在分析加載流程以前,有個前置概念要了解下,在通常狀況下,Spring
是經過反射機制利用 bean
的 class
屬性指定實現類來實例化 bean
。
引用書本:
在某些狀況下,實例化
bean
比較複雜,例若有多個參數的狀況下,傳統方式須要在配置文件中,寫不少配置信息,顯得不太靈活。在這種狀況下,可使用Spring
提供的FactoryBean
接口,用戶能夠經過實現該接口定製實例化bean
的邏輯。
FactoryBean
接口定義了三個方法:
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
複製代碼
主要講下用法吧:
當配置文件中的
<bean>
的class
屬性實現類是FactoryBean
時,經過getBean()
方法返回的不是FactoryBean
自己,而是FactoryBean#getObject()
方法返回的對象。
使用 demo
代碼請看下圖:
擴展 FactoryBean
以後,須要重載圖中的兩個方法,經過泛型約定返回的類型。在重載的方法中,進行本身個性化的處理。
在啓動類 Demo
,經過上下文獲取類的方法 context.getBean("beanName")
,使用區別是 beanName
是否使用 & 前綴,若是有沒有 & 前綴,識別到的是 FactoryBean.getObject 返回的 car
類型,若是帶上 & 前綴,那麼將會返回 FactoryBean
類型的類。
驗證和學習書中的概念,最快的方式是運行一遍示例代碼,看輸出結果是否符合預期,因此參考書中的例子,本身手打代碼,看最後的輸出結果,發現與書中說的一致,同時也加深了對 FactoryBean
的瞭解。
爲何要先講 BeanFactory
這個概念呢?
從時序圖看,在 1.5 那個步驟,調用了方法:
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
在這一步中,會判斷 sharedInstance
類型,若是屬於 FactoryBean
,將會調用用戶自定義 FactoryBean
的 getObject()
方法進行 bean
初始化。
實例化的真正類型是 getObjectType()
方法定義的類型,不是 FactoryBean
原來自己的類型。最終在容器中註冊的是 getObject()
返回的 bean
。
提早講了這個概念,但願你們在最後一步時不會對這個有所迷惑。
// Eagerly check singleton cache for manually registered singletons.
// 檢查緩存中或者實例工廠是否有對應的實例或者從 singletonFactories 中的 ObjectFactory 中獲取
Object sharedInstance = getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 檢查緩存中是否存在實例
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 記住,公共變量都須要加鎖操做,避免多線程併發修改
synchronized (this.singletonObjects) {
// 若是此 bean 正在加載則不處理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 當某些方法須要提早初始化,調用 addSingletonFactory 方法將對應的
// objectFactory 初始化策略存儲在 singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
複製代碼
單例模式在代碼設計中常常用到,在 Spring
中,同一個容器的單例只會被建立一次,後續再獲取 bean
直接從單例緩存 singletonObjects
中進行獲取。
並且由於單例緩存是公共變量,因此對它進行操做的時候,都進行了加鎖操做,避免了多線程併發修改或讀取的覆蓋操做。
還有這裏有個 earlySingletonObjects
變量,它也是單例緩存,也是用來保存 beanName
和 建立 bean
實例之間的關係。
與 singletonFactories
不一樣的是,當一個單例 bean
被放入到這 early
單例緩存後,就要從 singletonFactories
中移除,二者是互斥的,主要用來解決循環依賴的問題。(循環依賴下一篇再詳細講吧)
在 getBean
方法中,getObjectForBeanInstance
是個高頻方法,在單例緩存中得到 bean
仍是 根據不一樣 scope
策略加載 bean
,都有這個方法的出現,因此結合剛纔說的 BeanFactory
概念,一塊兒來看下這個方法作了什麼。
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
// 返回對應的實例,有時候存在諸如 BeanFactory 的狀況並非直接返回實例自己
// 而是返回指定方法返回的實例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
複製代碼
具體方法實現,搜索 註釋 4.6 看代碼中的註釋吧:
交代一下這個方法的流程:
bean
類型:判斷是不是工廠 bean
FactoryBean
不作處理bean
進行轉換FactoryBean
類型:委託給 getObjectFromFactoryBean
方法進行處理。在這個方法中,對工廠 bean
有特殊處理,處理方法跟上面提到的 FactoryBean
使用同樣,最終獲取的是 FactoryBean.getObject()
方法返回的類型。
對於第四個步驟,委託給 getObjectFromFactoryBean
方法進行處理不深刻分析,但裏面有三個方法值得一說:
// 單例操做,前置操做
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
// 單例模式,後置操做
afterSingletonCreation(beanName);
}
複製代碼
代碼中在類的加載時,有前置操做和後置操做,以前在第一篇筆記看過,不少前置和後置操做都是空方法,等用戶自定義擴展用的。
但在這裏的不是空方法,在兩個方法是用來保存和移除類加載的狀態,是用來對循環依賴進行檢測的。
同時,這兩個方法在不一樣 scope
加載 bean
時也有使用到,也是個高頻方法。
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
複製代碼
這是一個執行後處理的方法,我接觸的很少,先記下概念:
Spring 獲取 bean 的規則中有一條:儘量保證全部 bean 初始化後都會調用註冊的 BeanPostProcessor 的 postProcessAfterInitialization 方法進行處理。在實際開發中,能夠針對這個特性進行擴展。
如今來到時序圖中的 1.3 步驟:
// Create bean instance. 建立 bean 實例
// singleton 單例模式(最常使用)
if (mbd.isSingleton()) {
// 第二個參數的回調接口,接口是 org.springframework.beans.factory.ObjectFactory#getObject
// 接口實現的方法是 createBean(beanName, mbd, args)
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
// 省略了 try / catch
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
複製代碼
來看 getSingleton
方法作了什麼:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 註釋 4.7 全局變量,加鎖
synchronized (this.singletonObjects) {
// 檢查是否已經被加載了,單例模式就是能夠複用已經建立的 bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 初始化前操做,校驗是否 beanName 是否有別的線程在初始化,並加入初始化狀態中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
// 初始化 bean,這個就是剛纔的回調接口調用的方法,實際執行的是 createBean 方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 初始化後的操做,移除初始化狀態
afterSingletonCreation(beanName);
if (newSingleton) {
// 加入緩存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
複製代碼
來梳理一下流程:
beanName
的加載狀態bean
bean
過程當中所記錄到的各類輔助狀態對於第二步和第四步,在前面已經提到,用來記錄 bean
的加載狀態,是用來對 循環依賴 進行檢測的,這裏先略過不說。
關鍵的方法在於第三步,調用了 ObjectFactory
的 getObject()
方法,實際回調接口實現的是 createBean()
方法,須要往下了解,探祕 createBean()
。
bean
對於書中,有句話說的很到位:
在
Spring
源碼中,一個真正幹活的函數實際上是以do
開頭的,好比doGetBean
、doGEtObjectFromFactoryBean
,而入口函數,好比getObjectFromFactoryBean
,實際上是從全局角度去作統籌工做。
有了這個概念後,看以後的 Spring
源碼,都知道這個套路,在入口函數了解總體流程,而後重點關注 do
開頭的幹活方法。
按照這種套路,咱們來看這個入口方法 createBean()
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
RootBeanDefinition mbdToUse = mbd;
// 有道翻譯:確保此時bean類已經被解析,而且克隆 bean 定義,以防動態解析的類不能存儲在共享合併 bean 定義中。
// 鎖定 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.
// 驗證及準備覆蓋的方法
mbdToUse.prepareMethodOverrides();
// 讓 beanPostProcessor 有機會返回代理而不是目標bean實例。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
// 短路操做,若是代理成功建立 bean 後,直接返回
return bean;
}
// 建立 bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
複製代碼
先來總結這個流程:
lookup-method
和 replaced-method
屬性,而後這兩個配置的加載將會統一存放在 beanDefinition
中的 methodOverrides
屬性裏。下面來說下這幾個主要步驟
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exists.
if (hasMethodOverrides()) {
Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
synchronized (overrides) {
for (MethodOverride mo : overrides) {
// 處理 override 屬性
prepareMethodOverride(mo);
}
}
}
}
複製代碼
能夠看到,獲取類的重載方法列表,而後遍歷,一個一個進行處理。具體處理的是 lookup-method
和 replaced-method
屬性,這個步驟解析的配置將會存入 beanDefinition
中的 methodOverrides
屬性裏,是爲了待會實例化作準備。
若是 bean
在實例化時,監測到 methodOverrides
屬性,會動態地位當前 bean
生成代理,使用對應的攔截器爲 bean
作加強處理。
(我是不推薦在業務代碼中使用這種方式,定位問題和調用都太麻煩,一不當心就會弄錯=-=)
// 讓 beanPostProcessor 有機會返回代理而不是目標bean實例。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
// 短路操做,若是代理成功建立 bean 後,直接返回
return bean;
}
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;
}
複製代碼
在 doCreateBean
方法前,有一個短路操做,若是後處理器成功,將會返回代理的 bean
。
在 resolveBeforeInstantiation
方法中,在確保 bean
信息已經被解析完成,執行了兩個關鍵方法,從註釋中看到,一個是前置攔截器的操做,另外一個就是後置攔截器的操做。
若是第一個前置攔截器實例化成功,就已經將單例 bean
放入緩存中,它不會再經歷普通 bean
的建立過程,沒有機會進行後處理器的調用,因此在這裏的第二個步驟,就是爲了這個 bean
也能應用後處理器的 postProcessAfterInitialization
方法。
終於到了關鍵的幹活方法:doGetBean
。在經過上一個方法校驗,沒有特定的前置處理,因此它是一個普通 bean
, 常規 bean
進行建立在 doGetBean
方法中完成。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 註釋 4.8 根據指定 bean 使用對應的策略建立新的實例 例如跟進方法去看,有工廠方法,構造函數自動注入,簡單初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 容許後處理程序修改合併的bean定義
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
mbd.postProcessed = true;
}
}
// 是否須要提早曝光,用來解決循環依賴時使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 第二個參數是回調接口,實現的功能是將切面動態織入 bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
// 對 bean 進行填充,將各個屬性值注入
// 若是存在對其它 bean 的依賴,將會遞歸初始化依賴的 bean
populateBean(beanName, mbd, instanceWrapper);
// 調用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
// earlySingletonReference 只有在檢測到有循環依賴的狀況下才 不爲空
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 若是 exposedObject 沒有在初始化方法中被改變,也就是沒有被加強
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);
}
}
// bean 建立後,它所依賴的 bean 必定是已經建立了
// 在上面已經找到它有依賴的 bean,若是 actualDependentBeans 不爲空
// 表示還有依賴的 bean 沒有初始化完成,也就是存在循環依賴
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
}
// Register bean as disposable.
// 根據 scope 註冊 bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}
複製代碼
看到這麼長的代碼,感受有點頭暈,因此先來梳理這個方法的流程:
從上面流程能夠看出,這個方法作了不少事情,以致於代碼超過了 100 多行,給人的閱讀體驗差,因此儘可能仍是拆分小方法,在入口方法儘可能簡潔,說明作的事情,具體在小方法中完成。
由於這個建立過程的代碼不少和複雜,我挑重點來理解和學習,詳細的還有待深刻學習:
在上面第二個步驟,作的是實例化 bean
,而後返回 BeanWrapper
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 若是一個類有多個構造函數,每一個構造函數都有不一樣的參數,調用前須要進行判斷對應的構造函數或者工廠方法
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 若是已經解析過,不須要再次解析
if (resolved) {
if (autowireNecessary) {
// 實際解析的是 org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
// 構造函數自動注入(若是參數有不少個,在匹配構造函數可複雜了,不敢細看=-=)
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 使用默認的構造函數
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring? 須要根據參數解析構造函數
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
// 構造函數注入
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor. 沒有特殊的處理,使用默認構造函數構造
return instantiateBean(beanName, mbd);
}
複製代碼
大體介紹功能:
org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
在這個流程中,經過兩種方式,一種是工廠方法,另外一種就是構造函數,將傳進來的 RootBeanDefinition
中的配置二選一輩子成 bean
實例
具體的不往下跟蹤,來看下一個步驟
// 是否須要提早曝光,用來解決循環依賴時使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 第二個參數是回調接口,實現的功能是將切面動態織入 bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
複製代碼
關鍵方法是 addSingletonFactory
,完成的做用:在 bean
初始化完成前將建立實例的 ObjectFactory
加入單例工廠
一開始就講過, ObjectFactory
是建立對象時使用的工廠。在對象實例化時,會判斷本身依賴的對象是否已經建立好了,判斷的依據是查看依賴對象的 ObjectFactory
是否在單例緩存中,若是沒有建立將會先建立依賴的對象,而後將 ObjectFactory
放入單例緩存。
這時若是有循環依賴,須要提早對它進行暴露,讓依賴方找到並正常實例化。
循環依賴解決方案在下一篇再細講吧。
這也是個高頻方法,在初始化的時候要對屬性 property
進行注入,貼一些代碼片斷:
populateBean(beanName, mbd, instanceWrapper);
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 給 awareBeanPostProcessor 後處理器最後一次機會,在屬性設置以前修改bean的屬性
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
...
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
...
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
// 根據名字自動注入
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
// 根據類型自動注入
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 後處理器已經初始化
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// 須要依賴檢查
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
// 從 beanPostProcessors 對象中提取 BeanPostProcessor 結果集,遍歷後處理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
...
}
// 在前面也出現過,用來進行依賴檢查
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
checkDependencies(beanName, mbd, filteredPds, pvs);
// 將屬性應用到 bean 中,使用深拷貝,將子類的屬性一併拷貝
applyPropertyValues(beanName, mbd, bw, pvs);
}
複製代碼
因爲代碼太長,感興趣的小夥伴定位到 註釋 4.11 位置查看吧
介紹一下處理流程:
InstantiationAwareBeanPostProcessor
處理器的 postProcessAfterInstantiation
方法,判斷控制程序是否繼續進行屬性填充byName/byType
),提取依賴的 bean
,統一存入 PropertyValues
中BeanPostProcessor
和 依賴檢查:InstantiationAwareBeanPostProcessor
處理器的 postProcessProperties
方法,對屬性獲取完畢填充前,對屬性進行再次處理。checkDependencies
方法來進行依賴檢查PropertyValues
中的屬性填充至 BeanWrapper
中。在這個方法中,根據不一樣的注入類型進行屬性填充,而後調用後處理器進行處理,最終將屬性應用到 bean
中。
這裏也不細說,繼續往下走,看下一個方法
在配置文件中,在使用 <bean>
標籤時,使用到了 init-method
屬性,這個屬性的做用就是在這個地方使用的:bean
實例化前,調用 init-method
指定的方法來根據用戶業務進行相應的實例化。來看下入口方法 initializeBean
:
// 調用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 註釋 4.12 securityManage 是啥,不肯定=-=
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
// 若是沒有 securityManage,方法裏面校驗了 bean 的類型,須要引用 Aware 接口
// 對特殊的 bean 處理:Aware/ BeanClassLoader / BeanFactoryAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 熟悉麼,後處理器又來了
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
// 激活用戶自定義的 init-method 方法
invokeInitMethods(beanName, wrappedBean, mbd);
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
複製代碼
這個方法主要是用來進行咱們設定的初始化方法的調用,不過在方法內部,還作了其它操做,因此一塊兒來說下流程:
一、激活 Aware 方法
Spring
中提供了一些 Aware
接口,實現了這個接口的 bean
,在被初始化以後,能夠取得一些相對應的資源,例如 BeanFactoryAware
,在初始化後, Spring
容器將會注入 BeanFactory
的實例。因此若是須要獲取這些資源,請引用 Aware
接口。
二、執行後處理器
相信這個你們已經不陌生了,咱們能夠在諸如 PostProcessor
等後處理器裏面自定義,實現修改和擴展。例如 BeanPostProcessor
類中有 postProcessBeforeInitialization
和 postProcessAfterInitialization
,能夠對 bean
加載先後進行邏輯擴展,能夠將它理解成切面 AOP
的思想。
三、激活自定義的 init 方法
這個方法用途很明顯,就是找到用戶自定義的構造函數,而後調用它。要注意的是,若是 bean
是 InitializingBean
類型話,須要調用 afterPropertiesSet
方法。
執行順序是先 afterPropertiesSet
,接着纔是 init-method
定義的方法。
這是 Spring
提供銷燬方法的擴展入口,Spring
爸爸將咱們能考慮和想擴展的口子都給預留好。除了經過 destroy-method
屬性配置銷燬方法外,還能夠註冊後處理器 DestructionAwareBeanPostProcessor
來統一處理 bean
的銷燬方法:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
// 單例模式
// 註冊 DisposableBean
registerDisposableBean(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
else {
// A bean with a custom scope...
Scope scope = this.scopes.get(mbd.getScope());
scope.registerDestructionCallback(beanName,
new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}
複製代碼
這裏就是往不一樣的 scope
下, 進行 disposableBean
的註冊。
本篇筆記總結了類加載的過程,結合時序圖和代碼分析,但願對它能有一個更深的瞭解。
同時對代碼編寫也有一點感觸:
從一開始看 Spring
源碼的時候,就驚歎於它代碼的整潔和邏輯清晰,入口方法展現須要作的事情,而後工做具體邏輯細分,體現了代碼設計者的高超設計。
因此在看到有幾個方法超過 100 行,心中小小吐槽了一下,看來我跟大佬們寫的代碼也有共同點,那就是還能夠進行優化哈哈哈~
我截取的代碼片斷,因爲篇幅緣由,有些邏輯判斷和日誌處理都給摘掉了,可是日誌管理是很重要的一環,在關鍵地方打印日誌,在以後排查問題和分析數據都會有幫助。
若是懶得打印日誌,在關鍵的地方沒有打印日誌,即使出現了問題,也不知道從何查起,致使問題的緣由遲遲沒法暴露,形成用戶的投訴,那就得不償失了。
因爲我的技術有限,若是有理解不到位或者錯誤的地方,請留下評論,我會根據朋友們的建議進行修正
spring-analysis-note 碼雲 Gitee 地址
spring-analysis-note Github 地址
Spring 源碼深度解析 / 郝佳編著. -- 北京 : 人民郵電出版社