Spring:解決因@Async引起的循環依賴報錯


最近項目中使用@Async注解在方法上引起了循環依賴報錯:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Bean with name 'classA' has been injected into other beans [classB] 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

先提幾個問題:

1.@Async是什么

2.什么是循環依賴

3.為什么使用@async會引起循環依賴

4.如何解決

一、@Async

  從Spring3開始提供了@Async注解,該注解可以被標注在方法上(也可以標注在類上,代表所有方法都異步調用),以便異步地調用該方法。調用者將在調用時立即返回,方法的實際執行將提交給Spring TaskExecutor的任務中,由指定的線程池中的線程執行。

  看定義很簡單,其實就是新起一條線程執行注解下的方法,所以可以立即返回結果無需等待,常用於調用外部接口。

二、循環依賴

簡單講就是有A,B兩個服務,A依賴B,B依賴A,這樣就是一個死循環。如下圖:

 

 

用代碼表示:

 

@component
public class A{
    @Autowired
    private B b;
}

@component
public class B{
    @Autowired
    private A a;
}

 

  

三、為什么使用@async會引起循環依賴

其實當我們使用Spring的時候,默認是會解決循環依賴,但當使用了@Async注解方法后,處理循環依賴就失效了。

 

@Component
public class A{
     @Autowired
    private B b;

    @Async
    @Override
    public void testA() {

    }
}

 

@Component
public class ClassB{
    @Autowired
    private A a;

    @Override
    public void testB() {
        a.testA();
    }
}

如果要知道底層的前因后果,需要去分析源碼,這里我用別人總結的話簡單說一下:

1.context.getBean(A)開始創建A,A實例化完成后給A的依賴屬性b開始賦值
2.context.getBean(B)開始創建B,B實例化完成后給B的依賴屬性a開始賦值
3.重點:此時因為A支持循環依賴,所以會執行A的getEarlyBeanReference方法得到它的早期引用。而執行getEarlyBeanReference()的時候因為@Async根本還沒執行,所以最終返回的仍舊是原始對象的地址
4.B完成初始化、完成屬性的賦值,此時屬性field持有的是Bean A原始類型的引用
5.完成了A的屬性的賦值(此時已持有B的實例的引用),繼續執行初始化方法initializeBean(...),在此處會解析@Aysnc注解,從而生成一個代理對象,所以最終exposedObject是一個代理對象(而非原始對象)最終加入到容器里
6.尷尬場面出現了:B引用的屬性A是個原始對象,而此處准備return的實例A竟然是個代理對象,也就是說B引用的並非是最終對象(不是最終放進容器里的對象)
7.執行自檢程序:由於allowRawInjectionDespiteWrapping默認值是false,表示不允許上面不一致的情況發生,so最終就拋錯了

 

四、如何解決

1.使用@Lazy或者 @ComponentScan(lazyInit = true)

 

@Component
public class ClassB{

    @Autowired
    @Lazy
    private A a;

    @Override
    public void testB() {
        a.testA();
    }
}

 

2.使用setter注入

@Component
public class ClassA{

    private B b;

    public void setB(B b) {
        this.b = b;
    }

   @Async @Override
public void testA() { b.testb(); } }

 

@Component
public class ClassB{

    private A a;

    public void setA(A a) {
        this.a = a;
    }

    @Override
    public void testB() {
        a.testA();
    }
}

3.使用@Autowired注解

@Component
public class A{
     
    private B b;

    @Autowired
    public void SetB(B b) {
        this.b= b;
    }


    @Async
    @Override
    public void testA() {
        //TODO
    }
}    

4.重構方法

重新建class,把@Async的方法放在新的類中,從根本上消除循環依賴

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM