springboot2.6開始禁止循環依賴了

參考文章: https://mp.weixin.qq.com/s?__biz=MzI0MTUwOTgyOQ==&mid=2247497189&idx=1&sn=0f03cdafad9bacef66c64a490b85ff23&scene=21#wechat_redirect

使用了SpringBoot2.6及以上版本的,如果要允許循環依賴,可以作如下設置 :

方案二:允許循環引用

此方案更像是繞過問題而非解決問題本身!!!

它是一種妥協方案而非最佳實踐。在Spring Boot 2.6.0之前版本無需擔心此問題(默認允許循環引用),若你準備使用2.6.x但現實情況依舊必須允許循環引用那該怎麼辦呢?

有哪些現實情況呢?諸如:老項目升級Spring Boot版本需要保持向下兼容性;公司coder的水平不一,強制高標準的要求將會嚴重影響到生產效率等等

爲此,做法只有一個:禁用默認行爲(允許循環引用)。具體做法也很簡單,其實在文上啓動失敗的報錯詳情裏Spring Boot已非常貼心的告訴你了:圖片所以只需在配置文件application.properties里加上這個屬性:

spring.main.allow-circular-references = true

再次啓用Spring Boot 2.6.0版本的應用:正常啓動。

除了加屬性這個方法之外,也可以通過啓動類API的方式來設置,能達到同樣效果:

public static void main(String[] args) {
    new SpringApplicationBuilder(Application.class)
            .allowCircularReferences(true) // 允許循環引用
            .run(args);
}

圖片我們知道,允許循環引用與否其實是Spring Framework的能力,Spring Boot只是將其暴露爲屬性參數方便開發者來控制而已。那麼問題來了,如果是一個構建在純Spring Framework上的應用,如何禁止循環引用呢?你知道怎麼做嗎?歡迎在留言區討論作答,或私聊我探討學習~


加餐:允許循環引用了但依舊報錯

也許你一直認爲Spring已經解決循環引用問題了,所以在使用過程中可以“毫無顧忌”。非也,某些“特殊”場景下可能依舊會碰壁,並且問題還很隱蔽不好定位,不信你看我層層遞進的給你描述這個場景:

說明:以下代碼在允許循環引用的Spring Boot場景下演示運行

基礎代碼:

本例使用@PostConstruct來模擬觸發方法調用,效果和Controller裏調Service方法一樣哈

@Service
public class AService {

    @PostConstruct
    private void init() {
        String threadName = Thread.currentThread().getName();
        System.out.printf("線程號爲%s,開始調用業務fun方法\n", threadName);
        fun();

    }

    public void fun() {
        String threadName = Thread.currentThread().getName();
        System.out.printf("線程號爲%s,開始處理業務\n", threadName);
    }

}

啓動應用即觸發動作,控制檯輸出爲:

線程名爲main,開始調用業務fun方法
線程名爲main,fun方法開始處理業務

完美!此時,你發現fun方法執行時間太長,需要做異步化處理。你就立馬想到了使用Spring提供的@Async註解輕鬆搞定:

@Async
public void fun() {
 ...
}

再次運行,控制檯輸出:

線程名爲main,開始調用業務fun方法
線程名爲main,fun方法開始處理業務

what?木有生效呀!這時你靈機一動,原因是沒用開啓該模塊嘛。所以你迅速的使用@EnableAsync註解啓用Spring的異步模塊,滿懷期待的再次運行應用,控制檯輸出:

線程名爲main,開始調用業務fun方法
線程名爲main,fun方法開始處理業務

what a ...?怎麼還是不行。你撓了撓頭,想起來之前踩過的“事務不生效的坑”,場景和這類似,所以你模仿着採用了相同的方式來解決:自己注入自己(循環依賴)

@Autowired
private AService aService; // 自己注入自己

@PostConstruct
private void init() {
 ...
    aService.fun(); // 通過代理對象調用而非this調用

}

這次滿懷信心的再次運行,沒想到,啓動拋出BeanCurrentlyInCreationException異常

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [AService] 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.
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:649) ~[spring-beans-5.3.13.jar:5.3.13]
 ...

異常關鍵字:circular reference循環引用!!!不是說好了允許循環引用的嗎?怎麼肥四?怎麼破???

至此,筆者將此問題拋出,有興趣的同學可思考一下問題根因、解決方案哈。最終的效果應該是不同線程異步執行的:

線程名爲main,開始調用業務fun方法
線程名爲task-1,fun方法開始處理業務

Tips:筆者在之前的文章裏對此問題有過非常非常詳細的敘述,感興趣的可自行向前翻哈!!!主動學習😄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章