1. 程式人生 > 其它 >Spring原始碼解析之迴圈依賴

Spring原始碼解析之迴圈依賴

看個例子

CircularA類

@Service
public class CircularA {

    @Autowired
    private CircularB circularB;

    public CircularB getCircularB() {
        return circularB;
    }
}

CircularB類

@Service
public class CircularB {
    @Autowired
    private CircularA circularA;

    public CircularA getCircularA() {
        return circularA;
    }
}

測試方法

 @Test
    public void test3() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.com.dq");
        CircularA circularA = (CircularA) applicationContext.getBean("circularA");
        System.out.println(circularA.getCircularB());
        CircularB circularB = (CircularB) applicationContext.getBean("circularB");
        System.out.println(circularB.getCircularA());
    }

結果輸出

 


 
上述示例是我們編碼中很常見的一種編碼方式

 

迴圈依賴詳解

瞭解迴圈依賴,得先了解bean的例項化過程,bean的例項化請看作者寫的spring原始碼解析之bean的例項化

迴圈依賴流程

 


 

 

總結

A B迴圈依賴,優先例項化完成的是B,再是A,因為A在DI的時候需要B的例項,B沒有例項化就需要例項化,但是B在例項化的時候,DI的時候需要A,此時的A已經提前暴露,放在三級快取中,所以我們能夠拿到A的例項,然後注入到B中,B例項化完成以後,返回B的例項,然後A完成DI後,A就完成了例項化。

幾點思考

多例為什麼不能迴圈依賴?

因為多例情況下,A在例項化的時候,在依賴注入B的時候,B的例項有多個,依賴哪一個?
所以多例情況下,是不能迴圈依賴的
spring在原始碼中也做了處理,在快取中拿不到例項後,else首先就判斷了多例,多例的迴圈依賴直接會報錯

if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

通過構造器注入的方式,能迴圈依賴嗎?

看例子

CircularC 類
@Component
public class CircularC {

    private CircularD circularD;

    @Autowired
    public CircularC(CircularD circularD) {
        this.circularD = circularD;
    }
}
CircularD 類
@Component
public class CircularD {
    private CircularC circularC;

    @Autowired
    public CircularD(CircularC circularC) {
        this.circularC = circularC;
    }
}
執行結果

 


 
程式拋了異常
why?
C在例項化的時候,因為構造器的引數是引用型別,會觸發beanFactory.getBean的操作,觸發D類的例項化,此時D例項化,D例項化的時候同理會觸發C的例項化,此時三個快取中是沒有任何的C的資訊,因為三級快取的暴露是在構造器例項化完成之後,所以在快取中拿不到C,會繼續走例項化流程,但是此時singletonsCurrentlyInCreation已經有了C,因為C在第一次例項化的時候,將beanName加入到了該容器中,此時就會丟擲異常,原始碼如下:

 

protected void beforeSingletonCreation(String beanName) {
		//把beanName新增到singletonsCurrentlyInCreation Set容器中,在這個集合裡面的bean都是正在例項化的bean
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

所以構造器的方式的依賴注入是不能迴圈依賴的