更新時間:2021-06-16 12:35:23 來源:動力節點 瀏覽1063次
1. 循環依賴
什么是依賴注入?假設有兩個類A和B,A在實例化的時候需要B的實例,而B在實例化時又需要A的實例,在類的實例化過程就陷入死循環。這也就是傳統邏輯上的,“到底是先有雞,還是先有蛋”的問題?
下面舉一個例子,定義了兩個類Type和Org:
// Org.java
@Data
@Component
public class Org {
private final Role role;
public Org(Role role) {
this.role = role;
}
}
// Role.java
@Data
@Component
public class Role {
private final Org org;
public Role(Org org) {
this.org = org;
}
}
這是spring中典型的構造器注入方式,其實也代表了普通非spring bean之間,相互依賴時的實例化過程,但結果在運行的時候直接報循環依賴的錯誤:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
demoController (field private pers.kerry.exercise.springexercise.pojo.Org pers.kerry.exercise.springexercise.controller.DemoController.org)
┌─────┐
| org defined in file [/Users/kerry/code/idea/spring-exercise/target/classes/pers/kerry/exercise/springexercise/pojo/Org.class]
↑ ↓
| role defined in file [/Users/kerry/code/idea/spring-exercise/target/classes/pers/kerry/exercise/springexercise/pojo/Role.class]
└─────┘
而如果我們改一下代碼,把構造器注入方式改成基于屬性的注入(@Autowired、@Resouce),奇怪的是不報錯了,而且相互依賴的兩個bean 都實例化成功了。說明spring框架有解決循環依賴的問題,我們了解spring解決循環依賴的過程,其實有助于進一步了解spring 中 bean的活動過程。
2. 三級緩存
我們在之前介紹Bean的生命周期時說過,spring 中 bean的實例化過程,并非只是調用構造方法。除去spring框架本身提供的一些鉤子或擴展方法,簡單分成下面三個核心方法:
Spring在創建Bean的過程中分為三步
實例化,對應方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法,簡單理解就是new了一個對象。
屬性注入,對應方法:AbstractAutowireCapableBeanFactory的populateBean方法,為實例化中new出來的對象填充屬性和注入依賴。
初始化,對應方法:AbstractAutowireCapableBeanFactory的initializeBean,執行aware接口中的方法,初始化方法,完成AOP代理。
從單例Bean的初始化來看,主要可能發生循環依賴的環節就在第二步populate。值得注意的是,基于構造方法注入的方式,其實是將第一步和第二步同時進行,因此馬上就拋出錯誤。而spring通過基于屬性注入的方式,是否有其他特殊的處理呢,我們這時候就要提到spring的三級緩存:
private final Map singletonObjects = new ConcurrentHashMap<>(256);
private final Map earlySingletonObjects = new HashMap<>(16);
private final Map> singletonFactories = new HashMap<>(16);
3. 核心方法:getSingleton
我們在獲取bean實例的時候,其實是先從三級緩存中獲取,getBean 方法的邏輯如下:
Object sharedInstance = getSingleton(beanName);
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 查詢緩存中是否有創建好的單例
Object singletonObject = this.singletonObjects.get(beanName);
// 如果緩存不存在,判斷是否正在創建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 加鎖防止并發
synchronized (this.singletonObjects) {
// 從earlySingletonObjects中查詢是否有early緩存
singletonObject = this.earlySingletonObjects.get(beanName);
// early緩存也不存在,且允許early引用
if (singletonObject == null && allowEarlyReference) {
// 從單例工廠Map里查詢beanName
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// singletonFactory存在,則調用getObject方法拿到單例對象
singletonObject = singletonFactory.getObject();
// 將單例對象添加到early緩存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 移除單例工廠中對應的singletonFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
只針對單例的bean,多例的后面討論
默認的singletonObjects緩存不存在要get的beanName時,判斷beanName是否正在創建中
從early緩存earlySingletonObjects中再查詢,early緩存是用來緩存已實例化但未組裝完成的bean
如果early緩存也不存在,從singletonFactories中查找是否有beanName對應的ObjectFactory對象工廠
如果對象工廠存在,則調用getObject方法拿到bean對象
將bean對象加入early緩存,并移除singletonFactories的對象工廠
這是 getBean的邏輯,三級緩存中一級一級地找匹配的Bean,直到最后一級緩存,通過匹配beanName 的 ObjectFactory 來獲取Bean。那么singletonFactories何時放入了可以通過getObject獲得bean對象的ObjectFactory呢?
4. 核心方法:doCreateBean
Bean的實例化,實際執行的源碼是AbstractAutowireCapableBeanFactory類的doCreateBean方法:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// 1、創建一個對bean原始對象的包裝對象-BeanWrapper,執行createBeanInstance,即構造方法或工廠方法,給BeanWrapper賦值
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 2、允許其他修改beanDefinition,如使用Annotation增強Bean定義等,這通過類MergedBeanDefinitionPostProcessor來完成
synchronized(mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
} catch (Throwable var17) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);
}
mbd.postProcessed = true;
}
}
// 3、將當前bean 的 ObjetFactory放入singletonFactories中,
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
Object exposedObject = bean;
// 4、執行 populateBean,設置屬性值
// 5、執行 initializeBean,調用 Bean的初始化方法
try {
this.populateBean(beanName, mbd, instanceWrapper);
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
throw (BeanCreationException)var18;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
}
// 6、再次處理循環依賴問題
if (earlySingletonExposure) {
Object earlySingletonReference = this.getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
String[] dependentBeans = this.getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
String[] var12 = dependentBeans;
int var13 = dependentBeans.length;
for(int var14 = 0; var14 < var13; ++var14) {
String dependentBean = var12[var14];
if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// 7、注冊bean的銷毀回調方法,在beanFactory中注冊銷毀通知,以便在容器銷毀時,能夠做一些后續處理工作
try {
this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
} catch (BeanDefinitionValidationException var16) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
}
}
BeanWrapper
BeanWrapper接口,作為spring內部的一個核心接口,正如其名,它是bean的包裹類,即在內部中將會保存該bean的實例,提供其它一些擴展功能。同時,BeanWrapper接口還繼承了PropertyAccessor, propertyEditorRegistry, TypeConverter、ConfigurablePropertyAccessor接口,所以它還提供了訪問bean的屬性值、屬性編輯器注冊、類型轉換等功能。
我們回顧一下bean的實例化過程:
ResourceLoader加載配置信息
BeanDefinitionReader讀取并解析標簽,并將標簽的屬性轉換為BeanDefinition對應的屬性,并注冊到BeanDefinitionRegistry注冊表中。
容器掃描BeanDefinitionRegistry注冊表,通過反射機制獲取BeanFactoryPostProcessor類型的工廠后處理器,并用這個工廠后處理器對BeanDefinition進行加工。
根據處理過的BeanDefinition,實例化bean。然后BeanWrapper結合BeanDefinitionRegistry和PropertyEditorRegistry對Bean的屬性賦值。
以上就是動力節點小編介紹的"Spring的三級緩存和循環依賴",希望對大家有幫助,如有疑問,請在線咨詢,有專業老師隨時為您服務。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習