1.循環依賴
發生在bean的注冊過程中,bean A依賴于另一個bean B時,bean B依賴于bean A
代碼解釋:
這樣就是循環依賴。
不了解spring bean的建立請點選檢視
那麼是怎麼産生循環的呢?我們在學習了bean的建立的時候知道,bean對象的執行個體化完成之後才是進行初始化,在初始化的過程中進行了屬性的注入(依賴注入)。在AService中的屬性中有着BService對象,是以在注入屬性的時候,BService就作為了早期暴露bean(早期暴露指bean不在其他bean中,正常情況是一個一個bean進行注冊)。
循環依賴引用異常
在平常我們使用循環依賴時,一般不會出現循環依賴異常。因為底層已經幫我們進行了單例循環依賴的處理,那麼我們要怎麼去重制這個異常呢?
應該都注意到了底層是實作的單例,那麼我們可以用多例(原型模式)去重制。
AService.java:
package com.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AService {
@Autowired
BService bService;
}
BService.java:
package com.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BService {
@Autowired
AService aService;
}
Myconfig.java:
package com.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.service")
public class MyConfig {
}
Application.java:
package com;
import com.config.MyConfig;
import com.service.AService;
import com.service.BService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
AService aService = annotationConfigApplicationContext.getBean("AService", AService.class);
BService bService = annotationConfigApplicationContext.getBean("BService", BService.class);
}
}
點開
AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:274)
會發現是這一段代碼抛出的異常
循環依賴異常就重制了。那麼我們在項目中沒辦法隻能用多例,怎麼去解決循環依賴問題呢?
其實很簡單。這樣更改代碼即可:
AService.java:
package com.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class AService {
// @Autowired
BService bService;
public void setbService(BService bService) {
this.bService = bService;
}
}
BService.java:
package com.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class BService {
// @Autowired
AService aService;
public void setaService(AService aService) {
this.aService = aService;
}
}
Application.java:
package com;
import com.config.MyConfig;
import com.service.AService;
import com.service.BService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
AService aService = annotationConfigApplicationContext.getBean("AService", AService.class);
BService bService = annotationConfigApplicationContext.getBean("BService", BService.class);
aService.setbService(bService);
bService.setaService(aService);
}
}
我們手動去set對象,就可以解決多例的循環依賴問題。因為在底層中,每使用一次ASerivce/BService他都是不同的對象,是以找不到循環依賴的出口,導緻會死循環(當然,底層并不會死循環很久才報錯,而是通過特判抛出異常的)
基礎知識了解
執行個體化:代表對象隻是建立成功,并未對其屬性進行指派
初始化:代表對象建立成功,并已成功對其屬性進行指派
完整對象:初始化完成的對象
嬰兒對象:執行個體化完成的對象
循環依賴源碼檢視
了解過前面bean的建立的朋友,應該知道了,bean的初始化是建立對象之後進行屬性指派,而循環依賴的出現正是屬性指派過程中,是以我們這裡隻進行核心部分展示(循環依賴部分)
我們先從
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
開始看起。
在String beanName = transformedBeanName(name);中擷取beanName。
檢視getSingleton(beanName);方法,此方法是在緩存中查詢此bean是否注冊或者在注冊的過程中。下面我們檢視一下此方法内部邏輯
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 一級緩存(singletonObjects)查詢bean是否存在
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation判斷是否處于正在建立的途中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 二級緩存(earlySingletonObjects)查詢bean是否存在
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 一級緩存(singletonObjects)查詢bean是否存在
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 從二級緩存中取出bean對象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 從三級緩存中取出對象
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
然後在回到AbstractBeanFactory中doGetBean的if (isPrototypeCurrentlyInCreation(beanName)) 我們可以看到之前的循環引用異常的地方。
之後在看到
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
這裡有個Lambda 表達式,裡面涉及到了createBean,我們先進去看看這個表達式做了什麼
// 一級緩存中查詢
Object singletonObject = this.singletonObjects.get(beanName);
// 将此對象添加正在建立過程的bean集合singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
try {
// 調用Lambda 表達式中的createBean(beanName, mbd, args)
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
進入之後調用doCreateBean
進入之後有這麼一段
if (instanceWrapper == null) {
// 通過反射建立bean執行個體
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
再往下走
// 添加嬰兒對象到三級緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 添加到三級緩存
this.singletonFactories.put(beanName, singletonFactory);
//移除二級緩存中的bean(二級和三級不共存)this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
然後就到達了我們最熟悉的代碼
try {
// 屬性注入
populateBean(beanName, mbd, instanceWrapper);
// 代理
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
再進入populateBean到
// 屬性注入
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
因為要建立對象是以最後傳回到了protected T doGetBean,用于建立BService。再一次循環走上面的步驟直到再一次打算建立AService。直到到了protected Object getSingleton(String beanName, boolean allowEarlyReference) 方法發生了變化。
直接進入到了最底層代碼,将AService從三級緩存釋放,放入到二級緩存,因為三級緩存中存在AService的嬰兒對象,也就是執行個體化的對象AService,是以在BService屬性注入的時候,将嬰兒對象AService指派給了BService的屬性。那麼BService的初始化就直接完成了。
那麼BService直接進入這個方法,進行二次處理
if (earlySingletonExposure) {
// 對bean進行第二次處理
Object earlySingletonReference = getSingleton(beanName, false);
之後沒什麼特别重要的。。。
最後來到DefaultSingletonBeanRegistry中public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)的
if (newSingleton) {
// 将BService添加到一級緩存,删除二級緩存BService
addSingleton(beanName, singletonObject);
}
之後跳出所有關于BService的方法。繼續進行AService方法的調用,最後在
這個set中注入BService。至此,循環依賴已經結束
接下來進行和之前一樣的操作,将AService放在一級緩存,移除三級緩存的資料。可能會有人疑問了,貌似并不需要三級緩存,隻用一級和二級緩存即可完成循環依賴操作。這個問題放在循環依賴(二)來講,細心的會發現我在前面提到了二次處理,這個疑問也下次再解答