一文詳解spring迴圈依賴
一文詳解Spring迴圈依賴
什麼是迴圈依賴?
大家都知道spring的核心是一個實現了AOP的IOC容器,那麼IOC容器對於bean的初始化,會遇到以下情況:當BeanA初始化時,它依賴的物件BeanB也需要執行初始化,如果BeanB裡也依賴了BeanA,則又會開始執行BeanA的初始化,那麼這樣會無限迴圈,導致初始化異常如下所示。
Spring已經很好的解決了這個問題,這個解決方法就是三級快取。
什麼是三級快取?
我們以上圖中A、B互相依賴為例,spring為了解決迴圈依賴問題,做了以下步驟:
-
將A通過反射建立的“初級bean”a放入到三級快取中,再執行a的屬性填充,這時發現依賴B,開啟B的初始化。
-
B也通過也反射生成“初級bean”b放入到三級快取中,再執行b的屬性填充,這時發現依賴A,開啟A的初始化。
-
從三級快取中找到a,A不再建立新物件,把它移動到二級快取中,返回a。
-
b拿到a的引用,設定到b對應的欄位上,屬性填充完成,將b從三級快取暴露到一級快取中,返回b。
-
a拿到b的引用,設定到a對應的欄位上,屬性填充完成,將a從二級快取暴露到一級快取中,返回a,A對應的例項Bean初始化完成。
其簡易時序圖:
邏輯圖如下:
我們們再看看三級快取的儲存結構:
/** Cache of singleton objects: bean name to bean instance. */
/** 一級快取,初始化完成的SpringBean均放置其中 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
/** 二級快取,反射完成後,還未填充屬性的初級物件但是其他物件查詢過時從三級中移動到二級 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
/** 三級快取,反射完成後,還未填充屬性的初級物件放置其中 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
為什麼三級快取earlySingletonObjects和二級快取singletonFactories的初始容量16,而一級快取容量為256呢?筆者認為因為二級、三級僅僅是在處理依賴時會使用到,這種多重迴圈依賴的情況在實際專案中應該是少數,所以不用使用太大的空間。而最終spring例項化完成的bean會放置在一級快取中,所以預設容量會調大一些,畢竟spring有很多自身的bean也是放置在這裡面的,比如systemEnvironment、systemProperties、messageSource、applicationEventMulticaster等。
spring的原始碼閱讀
- 當單例物件不存在時,會通過org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)方法來獲取單例物件。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
/** 省略部分程式碼 */
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
// 在一級快取singletonObjects中拿到為空
if (singletonObject == null) {
/** 省略狀態檢查部分程式碼 */
boolean newSingleton = false;
try {
// 傳進來的呼叫,lamda表示式使用
singletonObject = singletonFactory.getObject();
// *********重要*********:singletonFactory.getObject()執行完畢,標記此類已經初始化完成
// bean初始化完成,標記為新的單例物件
newSingleton = true;
}
catch (IllegalStateException ex) {
/** 省略部分程式碼 */
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
// 如果是新的單例物件,暴露到一級快取中
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
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);
}
}
- 上面程式碼中的singletonFactory.getObject() 無疑是執行建立的關鍵程式碼:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])方法
/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 拿到Bd
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<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
// 檢查該bean是否有過載方法
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
/** 省略部分程式碼 */
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// 嘗試獲取代理物件;
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
/** 省略部分程式碼 */
}
try {
// 進入,真真正正建立bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
catch (Throwable ex) {
/** 省略部分程式碼 */
}
}
- 再來看看doCreateBean方法
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 建立 Bean 例項,僅僅呼叫構造方法,但是尚未設定屬性
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
/** 省略部分程式碼 */
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 暴露到三級快取中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 初始化bean例項
Object exposedObject = bean;
try {
// Bean屬性填充
populateBean(beanName, mbd, instanceWrapper);
// 呼叫初始化方法,應用BeanPostProcessor後置處理器
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
/** 省略部分程式碼 */
}
if (earlySingletonExposure) {
// 呼叫一次getSingleton(beanName, false)方法->" + beanName),只從一級、二級快取中拿,傳入false不需要從三級新增到二級快取;
// 核心邏輯是:如果提前暴露到了二級,則返回二級快取中的物件引用,此時可能獲取得到的是原物件的代理物件。因為AOP動態代理時,會將物件提升二級快取,本文不再詳述此問題
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
/** 省略部分程式碼,檢查依賴物件是否均建立完成 */
}
}
}
// Register bean as disposable.
try {
// 初始化完成後一些註冊操作
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
/** 省略部分程式碼 */
}
return exposedObject;
}
從doCreateBean方法可以看出:先呼叫構造方法,生成初級bean,然後暴露到三級快取,然後執行屬性填充,最表標記bean初始化完成,如果二級快取有,則替換引用,最後完成註冊並返回物件。
- 那麼這個填充屬性方法populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 又做了什麼呢?
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
/** 省略部分程式碼 */
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;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
程式碼比較多,核心思想就是獲取這個bean裡的所有依賴bean,然後呼叫applyPropertyValues方法去建立對應的依賴bean,並設定到對應的屬性上。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
/** 省略部分程式碼 */
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
// *** 將依賴的屬性目標,轉化為初始化完成後的bean
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
/** 省略部分程式碼 */
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
/** 省略部分程式碼 */
}
}
/** 省略部分程式碼 */
}
valueResolver.resolveValueIfNecessary方法經過一些的方法,最終呼叫beanFactory.getBean,這個方法會回到開始進行新一輪的建立bean
private Object resolveInnerBean(Object argName, String innerBeanName, BeanDefinition innerBd) {
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
this.beanFactory.registerDependentBean(dependsOnBean, actualInnerBeanName);
// 初始化bean
this.beanFactory.getBean(dependsOnBean);
}
}
}
- allowEarlyReference傳入true,對於新的bean,已經在三級快取中存在,會將三級快取轉移到二級快取,並返回bean,不用真正的去建立一個bean。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
boolean needWarn = true;
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
logger.warn("當前bean已註冊,從一級earlySingletonObjects中拿不到->" + beanName + ":" + singletonObject);
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
logger.warn("當前bean已註冊,從二級快取earlySingletonObjects中拿不到->" + beanName + ":" + singletonObject);
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
needWarn = false;
logger.warn("當前bean已註冊,從三級singletonFactories中拿到,並移動到二級快取earlySingletonObjects->" + beanName + " : " + singletonObject);
}
}
}
}
if (needWarn) {
logger.warn("從三級快取中查詢,呼叫DefaultSingletonBeanRegistry.getSingleton(beanName, allowEarlyReference)->得到" + beanName + ":" + singletonObject + " ,allowEarlyReference:" + allowEarlyReference);
}
return singletonObject;
}
- 所以第三步的Bean B屬性填充方法此時完成,Bean B被載入到一級快取中。由此回溯,Bean A的屬性填充完成,Bean A被載入到一級快取中。可結合本文最開始給出的時序圖進行參考。
致謝
本文參考以下來源文章:
https://zhuanlan.zhihu.com/p/223028634?utm_source=wechat_session
相關文章
- spring解決迴圈依賴Spring
- Spring迴圈依賴Spring
- 【Spring系列】- Spring迴圈依賴Spring
- Spring IoC - 迴圈依賴Spring
- Spring怎麼解決迴圈依賴?Spring
- 【Spring】Spring中的迴圈依賴及解決Spring
- 【spring】迴圈依賴 Java Vs SpringSpringJava
- 再探迴圈依賴 → Spring 是如何判定原型迴圈依賴和構造方法迴圈依賴的?Spring原型構造方法
- Spring迴圈依賴+案例解析Spring
- Spring中的迴圈依賴Spring
- 【Spring】快速理解迴圈依賴Spring
- Spring 中 bean 的迴圈依賴SpringBean
- Spring如何解決迴圈依賴?Spring
- Spring 迴圈依賴的三種方式(三級快取解決Set迴圈依賴問題)Spring快取
- Spring中如何解決迴圈依賴Spring
- 3.1 spring5原始碼系列--迴圈依賴 之 手寫程式碼模擬spring迴圈依賴Spring原始碼
- Spring原始碼分析:Spring的迴圈依賴分析Spring原始碼
- SpringIOC迴圈依賴Spring
- 24--Spring解決bean之間的迴圈依賴SpringBean
- Spring原始碼分析之迴圈依賴及解決方案Spring原始碼
- Spring如何使用三級快取解決迴圈依賴Spring快取
- spring是如何解決迴圈依賴的?Spring
- Spring原始碼分析之IOC迴圈依賴Spring原始碼
- 幫助你更好的理解Spring迴圈依賴Spring
- Spring是如何解決迴圈依賴的Spring
- Spring學習:簡單實現一個依賴注入和迴圈依賴的解決Spring依賴注入
- spring迴圈依賴解決過程&Bean的生命週期SpringBean
- 淺談迴圈依賴
- 面試題:Spring 的迴圈依賴問題面試題Spring
- Spring 是怎麼處理迴圈依賴的?Spring
- 【spring原始碼系列】之【Bean的迴圈依賴】Spring原始碼Bean
- 關於我對Spring迴圈依賴的思考Spring
- 一張圖徹底搞懂Spring迴圈依賴Spring
- spring: 我是如何解決迴圈依賴的?Spring
- 徹底理解Spring如何解決迴圈依賴Spring
- 關於spring迴圈依賴的一點小感悟Spring
- spring原始碼閱讀筆記09:迴圈依賴Spring原始碼筆記
- Spring——為什麼會有迴圈依賴(原始碼)Spring原始碼