本文主要研究getBean的流程。
1.首先嚐試框架會從容器的緩存裏獲取單例Bean實例,這個單例Bean有可能是一個普通Bean,也有可能是一個FactoryBean,然後調用他的getObject方法返回。
2.如果這裏1獲取不到,不管是單例還是原型模式,都要框架另外創建實例了。然後進行循環依賴的判斷邏輯,正是因爲循環依賴,纔會用到三級緩存,
3.如果該容器中沒有該Bean的BeanDefiniton實例,則遞歸去父容器去查找。
4.如果該容器中有該Bean的BeanDefiniton實例,則獲取Bean實例。
5.遞歸實例化顯式依賴的Bean。顯式依賴在其BeanDefiniton設置了Depends-On屬性。假設A Depends - On B,那麼B 就會先於A實例化。
6.根據不同的Scope選擇不同的策略創建Bean實例。
7.對Bean經行類型檢查。
下面直接看看代碼吧
這行代碼直接返回Bean的名字,這裏支持別名獲取,和加了&前綴獲取生產的Bean的工廠類本身。
然後嘗試從緩存中獲取Bean實例。
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//嘗試從一級緩存獲取Bean實例,singletonObjects就是單例Bean的一級緩存
Object singletonObject = this.singletonObjects.get(beanName);
//如果完整的Bean實例美與被創建出來再一級緩存,那麼就會將其加入到SingletonCurrentlyInCreation中
//如果一級緩存沒有,且這個Bean正在創建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//對一級緩存加鎖,防止幾個線程對緩存同時 操作,導致數據對不上。
synchronized (this.singletonObjects) {
//嘗試從二級緩存中獲取Bean實例,注意這裏的二級和後面的三級緩存不是ConcurrentMap,具體原因自己思考吧
singletonObject = this.earlySingletonObjects.get(beanName);
//如果二級緩存也沒有,且存在早期引用,說明是循環依賴。
if (singletonObject == null && allowEarlyReference) {
//從三級緩存獲取創建Bean實例的工廠
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果哦從三級緩存獲取到的工廠 不爲空,則從工廠裏面獲取Bean實例
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//放到二級緩存
this.earlySingletonObjects.put(beanName, singletonObject);
//從三級緩存刪除,保證緩存的一致性
//注意:只有一級緩存保存的是完整的Bean.
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
到了這裏,邏輯上差不多結束了。然後Spring會根據FactoryBean前綴判斷到底需要獲取 的是工廠類還是.Bean本身。
以上邏輯是單例Bean的獲取,和從緩存中獲取到實例的情況。如果沒有呢,那麼就會繼續就會執行下面的邏輯。
// 可以看到原型模式也有一個註冊列表,如果此時還在註冊表中,說明是循環依賴,原型模式的循環依賴誤無解,直接拋出異常。而這裏的註冊表是ThreadLocal對象,也就是說在一個線程中出現了循環依賴,無解。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
如果從當前容器中找不到這個Bean的BeanDefinition,且該容器存在父容器,則遞歸從父容器尋找。
找到BeanDefiniton,如果BeanDefiniton裏面具有顯示的循環依賴,拋出異常 。
然後根據不同的Scope的Bean創建。
創建完成後,在一級緩存中加入Bean實例,並且在二級三級刪除。在已註冊名單中加入這個Bean.正創建名單中清楚這個Bean