springboot配置允許循環(huán)依賴問題
springboot配置允許循環(huán)依賴
循環(huán)依賴報錯
當啟動項目時,可能出現(xiàn)程序不能啟動的情況,查看調(diào)試日志
會提示 :
The dependencies of some of the beans in the application context form a cycle...
原因
SpringBoot 從 2.6 開始默認不允許出現(xiàn) Bean 循環(huán)引用。而且這個是在Bean 定義上也就是類上就不允許出現(xiàn)循環(huán)引用。
解決方式
1、允許循環(huán)引用存在
方式1.在全局配置文件設(shè)置允許循環(huán)引用存在:
spring: ? main: ? ? allow-circular-references:true? ? ?
方式2.在SpringApplicationBuilder 添加設(shè)置允許循環(huán)引用:
public static void main(String[] args) { ? new SpringApplicationBuilder(DemoApplication.class).allowCircularReferences(true).run(args); }? ? ? ??
2、消除循環(huán)依賴
Spring官方的初心是不希望開發(fā)者編寫循環(huán)引用的代碼,也就是說未來的某個版本可能強制不能使用循環(huán)引用,因此在項目中消除循環(huán)依賴是不得不面對的問題。
面試官:”Spring是如何解決的循環(huán)依賴?“
答:Spring通過三級緩存解決了循環(huán)依賴,其中一級緩存為單例池(singletonObjects),二級緩存為早期曝光對象earlySingletonObjects,三級緩存為早期曝光對象工廠(singletonFactories)。
當A、B兩個類發(fā)生循環(huán)引用時,在A完成實例化后,就使用實例化后的對象去創(chuàng)建一個對象工廠,并添加到三級緩存中,如果A被AOP代理,那么通過這個工廠獲取到的就是A代理后的對象,如果A沒有被AOP代理,那么這個工廠獲取到的就是A實例化的對象。
當A進行屬性注入時,會去創(chuàng)建B,同時B又依賴了A,所以創(chuàng)建B的同時又會去調(diào)用getBean(a)來獲取需要的依賴,此時的getBean(a)會從緩存中獲取,第一步,先獲取到三級緩存中的工廠;第二步,調(diào)用對象工工廠的getObject方法來獲取到對應(yīng)的對象,得到這個對象后將其注入到B中。
緊接著B會走完它的生命周期流程,包括初始化、后置處理器等。當B創(chuàng)建完后,會將B再注入到A中,此時A再完成它的整個生命周期。至此,循環(huán)依賴結(jié)束!
面試官:”為什么要使用三級緩存呢?二級緩存能解決循環(huán)依賴嗎?“
答:如果要使用二級緩存解決循環(huán)依賴,意味著所有Bean在實例化后就要完成AOP代理,這樣違背了Spring設(shè)計的原則,Spring在設(shè)計之初就是通過AnnotationAwareAspectJAutoProxyCreator這個后置處理器來在Bean生命周期的最后一步來完成AOP代理,而不是在實例化后就立馬進行AOP代理。
SpringBoot禁用循環(huán)依賴
Spring的Bean管理,一直是整個體系中津津樂道的東西。尤其是Bean的循環(huán)依賴,更是很多面試官最喜歡考察的2B知識點之一。
但事實上,項目中存在Bean的循環(huán)依賴,是代碼質(zhì)量低下的表現(xiàn)。多數(shù)人寄希望于框架層來給擦屁股,造成了整個代碼的設(shè)計越來越糟,最后用一些奇技淫巧來填補犯下的錯誤。
還好,SpringBoot終于受不了這種濫用,默認把循環(huán)依賴給禁用了!
從2.6版本開始,如果你的項目里還存在循環(huán)依賴,SpringBoot將拒絕啟動!
驗證代碼小片段
為了驗證這個功能,我們只需要兩段小代碼。
CircularDependencyA.java
@Component @RequiredArgsConstructor public class CircularDependencyA { private final CircularDependencyB circB; }
CircularDependencyB.java
@Component @RequiredArgsConstructor public class CircularDependencyB { private final CircularDependencyA circA; }
RequiredArgsConstructor注解,是lombok包里面的,用來實現(xiàn)簡單的構(gòu)造器注入。不出所料,當我們啟動代碼的時候,報錯了~~
報錯如下:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| circularDependencyA defined in file [cir/CircularDependencyA.class]
↑ ↓
| circularDependencyB defined in file [cir/CircularDependencyB.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
當然,有些鳥人已經(jīng)玩大了,循環(huán)依賴到處都是,改代碼變的越來越不現(xiàn)實。那你還可以通過在yaml里配置參數(shù)來臨時開啟循環(huán)依賴。
spring.main.allow-circular-references=true
看來SpringBoot對惡勢力的容忍能力還是不夠堅決??!
繞過SpringBoot這個攔截的方法不止一種,比如使用@Lazy注解進行延遲初始化。但這些都是治標不治本,辜負了SpringBoot的一片苦心。
做對的事
其實,我們一直把代碼往下找下去,會發(fā)現(xiàn)這個開關(guān),其實是Spring的功能。
AbstractAutowireCapableBeanFactory#allowCircularReferences /** Whether to automatically try to resolve circular references between beans. */ private boolean allowCircularReferences = true;
很長一段時間,SpringBoot這個值都是默認為true的。但這種縱容造成了大批低質(zhì)量的代碼產(chǎn)生,以至于新員工一直在給老員工擦屁股。
把這個值默認設(shè)置為false,是堅持做對的事情。起碼,在工程師編寫出質(zhì)量不高的代碼時,能夠知道他自己在做什么,而不是把隱患一步步的推遲,任代碼腐敗。
不得不為SpringBoot點個贊。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn)
這篇文章主要介紹了Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-03-03Java 發(fā)送http請求(get、post)的示例
這篇文章主要介紹了Java 發(fā)送http請求的示例,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10如何利用IDEA搭建SpringBoot項目整合mybatis實現(xiàn)簡單的登錄功能
這篇文章主要介紹了如何利用IDEA搭建SpringBoot項目整合mybatis實現(xiàn)簡單的登錄功能,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08關(guān)于Java 項目封裝sqlite連接池操作持久化數(shù)據(jù)的方法
這篇文章主要介紹了Java 項目封裝sqlite連接池操作持久化數(shù)據(jù)的方法,文中給大家介紹了sqlite的體系結(jié)構(gòu)及封裝java的sqlite連接池的詳細過程,需要的朋友可以參考下2021-11-11Idea連接GitLab的過程以及創(chuàng)建在gitlab中創(chuàng)建用戶和群組方式
本文介紹了如何在IDEA中連接GitLab,首先需安裝GitLab插件并配置SSH免密登錄,接著,創(chuàng)建GitLab個人令牌并在Git中配置,文章還提到了如何在GitLab中創(chuàng)建用戶、群組及設(shè)置權(quán)限,如Owner、Maintainer、Developer等,并強調(diào)了群組名和人員名稱的命名規(guī)范2024-11-11