SpringBoot IoC(5)循環依賴與實例化總結

所謂的循環依賴,是指在實例化A類時,注入B類,實例化B類時,注入A類。bash

依賴注入分爲構造器注入和字段注入,下面依次對這兩種狀況分析。ui

構造器注入時

@Configuration
@RequiredArgsConstructor
public class Adam {
    private final Eve eve;
}
複製代碼
@RequiredArgsConstructor
@Configuration
public class Eve {
    private final Adam adam;
}
複製代碼
AbstractAutowireCapableBeanFactory->createBeanInstance():
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
複製代碼

在選擇構造器注入的時候,會遍歷字段,根據beanClass反推beanName,而後實例化。 一路debug,最終找到了拋出異常的調用棧:this

DefaultSingletonBeanRegistry->getSingleton():
    beforeSingletonCreation(beanName);
DefaultSingletonBeanRegistry->beforeSingletonCreation():
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
複製代碼

與之對應的是afterSingletonCreation方法spa

if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
複製代碼

結論

singletonsCurrentlyInCreation是一個Set集合,表示當前正在建立的bean名稱,建立完成後,會移除。
原來,當同一個bean未完成實例化,又再一次實例化時,singletonsCurrentlyInCreation.add(beanName)返回false,進而拋出了異常,致使程序報錯debug

字段注入時

@Component
public class Adam {
    @Autowired
    private eve;
}
複製代碼
@Component
public class Eve {
    @Autowired
    private Adam adam;
}
複製代碼

這樣寫發現並無報錯,一路debug,發現是這段代碼起的做用:code

AbstractBeanFactory->doGetBean():
    Object sharedInstance = getSingleton(beanName);
AbstractBeanFactory->getSingleton():
    return getSingleton(beanName, true);
AbstractBeanFactory->getSingleton(): 
    // 此時singletonObjects未完成,因此確定是null
    Object singletonObject = this.singletonObjects.get(beanName);
    // 根據前面的分析,singletonsCurrentlyInCreation確定包含beanName
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
複製代碼

此時發現,earlySingletonObjects.get(beanName)爲空,singletonFactories.get(beanName)獲取到了值,繼而獲取到了singletonObject(這裏看出earlySingletonObjects建立,對應的singletonFactory會移除)。從新debug,發現是這裏給singletonFactories賦了值:cdn

AbstractAutowireCapableBeanFactory->doCreateBean():
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
DefaultSingletonBeanRegistry->addSingletonFactory():
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
複製代碼

這段代碼是在實例化完成以後進行的,也就是說,當構造器注入的時候,並無添加到singletonFactories,因此getSingleton(beanName)返回空,進行了第二次的實例化,從而致使報錯(這裏看出singletonFactories建立,對應的earlySingletonObjects會移除)。繼續debug,最終singletonFactories和earlySingletonObjects都被清除了:blog

DefaultSingletonBeanRegistry->getSingleton():
    addSingleton(beanName, singletonObject);
DefaultSingletonBeanRegistry->addSingleton():
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
複製代碼

當singletonObjects建立的時候,earlySingletonObjects和singletonFactories的使命就完成了~ci

結論

對於字段注入,IoC容器使用了singletonFactories和earlySingletonObjects來預防循環依賴。rem

附錄:正常狀況下一個bean的實例化過程

此處漏掉了AoP的邏輯,後續補充
相關文章
相關標籤/搜索