詳解Spring Bean的循環(huán)依賴解決方案
如果使用構(gòu)造函數(shù)注入,則可能會創(chuàng)建一個(gè)無法解析的循環(huán)依賴場景。
什么是循環(huán)依賴
循環(huán)依賴其實(shí)就是循環(huán)引用,也就是兩個(gè)或則兩個(gè)以上的bean互相持有對方,最終形成閉環(huán)。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:

注意,這里不是函數(shù)的循環(huán)調(diào)用,是對象的相互依賴關(guān)系。循環(huán)調(diào)用其實(shí)就是一個(gè)死循環(huán),除非有終結(jié)條件。
Spring中循環(huán)依賴場景有:
(1)構(gòu)造器的循環(huán)依賴
(2)field屬性的循環(huán)依賴。
怎么檢測是否存在循環(huán)依賴
檢測循環(huán)依賴相對比較容易,Bean在創(chuàng)建的時(shí)候可以給該Bean打標(biāo),如果遞歸調(diào)用回來發(fā)現(xiàn)正在創(chuàng)建中的話,即說明了循環(huán)依賴了。
下面是我所遇到的情況,代碼結(jié)構(gòu)如下:
SpringSecurity 配置類:
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
/**
* 通過配置類構(gòu)造函數(shù)注入 UserDetailsService
*/
@Autowired
public BrowserSecurityConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
/**
* 在配置類中聲明 加密編碼器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
... ...
}
UserDetailsService 類:
@Component
public class MyUserDetailService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 通過構(gòu)造函數(shù)注入 PasswordEncoder
*/
@Autowired
public MyUserDetailService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
... ...
}
運(yùn)行之后,Spring拋出了如下錯(cuò)誤信息:
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| browserSecurityConfig defined in file [D:\CODE\Java\IdeaProjects\mango-security\mango-security-browser\target\classes\stu\mango\security\browser\BrowserSecurityConfig.class]
↑ ↓
| myUserDetailService defined in file [D:\CODE\Java\IdeaProjects\mango-security\mango-security-browser\target\classes\stu\mango\security\browser\MyUserDetailService.class]
└─────┘
該例中,BrowserSecurityConfig 通過構(gòu)造函數(shù)注入 UserDetailsService實(shí)例,而 UserDetailsService由通過構(gòu)造函數(shù)注入在BrowserSecurityConfig 中聲明的PasswordEncoder。
總結(jié)來說,Spring Bean的循環(huán)依賴是指,類A需要通過構(gòu)造函數(shù)注入的類B的實(shí)例(或者B中聲明的Bean),而類B需要通過構(gòu)造函數(shù)注入的類A的實(shí)例(或者A中聲明的Bean)。如果將類A和類B的bean配置為相互注入,則Spring IoC容器會在運(yùn)行時(shí)檢測到此循環(huán)引用,并引發(fā)一個(gè)BeanCurrentlyInCreationException。與典型情況(沒有循環(huán)依賴)不同,bean A和bean B之間的循環(huán)依賴關(guān)系迫使其中一個(gè)bean在被完全初始化之前被注入到另一個(gè)bean中(這是一個(gè)典型的“先有雞還是先有蛋”場景)。
解決方案
簡明扼要的說,就是——不使用基于構(gòu)造函數(shù)的依賴注入??赏ㄟ^下面方式解決。
在字段上使用@Autowired注解,讓Spring決定在合適的時(shí)機(jī)注入?!就扑]】
用基于setter方法的依賴注射取代基于構(gòu)造函數(shù)的依賴注入來解決循環(huán)依賴。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java?熱更新?Groovy?實(shí)踐及踩坑指南(推薦)
Apache的Groovy是Java平臺上設(shè)計(jì)的面向?qū)ο缶幊陶Z言,這門動態(tài)語言擁有類似Python、Ruby和Smalltalk中的一些特性,可以作為Java平臺的腳本語言使用,這篇文章主要介紹了Java?熱更新?Groovy?實(shí)踐及踩坑指南,需要的朋友可以參考下2022-09-09
springmvc Controller方法沒有加@ResponseBody導(dǎo)致api訪問404問題
這篇文章主要介紹了springmvc Controller方法沒有加@ResponseBody導(dǎo)致api訪問404問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
java地理坐標(biāo)系及投影間轉(zhuǎn)換代碼示例
在地圖投影中,經(jīng)常需要將坐標(biāo)從不同的坐標(biāo)系之間進(jìn)行轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于java地理坐標(biāo)系及投影間轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2024-08-08
Spring Boot項(xiàng)目中實(shí)現(xiàn)文件上傳功能的示例
這篇文章主要介紹了Spring Boot項(xiàng)目中實(shí)現(xiàn)文件上傳功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
Java使用freemarker實(shí)現(xiàn)word下載方式
文章介紹了如何使用FreeMarker實(shí)現(xiàn)Word文件下載,包括引用依賴、創(chuàng)建Word模板、將Word文件存為XML格式、更改后綴為FTL模板、處理圖片和代碼實(shí)現(xiàn)2025-02-02
MyBatis-Plus自動填充功能失效導(dǎo)致的原因及解決
這篇文章主要介紹了MyBatis-Plus自動填充功能失效導(dǎo)致的原因及解決,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
spring?boot?executable?jar/war?原理解析
spring boot里其實(shí)不僅可以直接以 java -jar demo.jar的方式啟動,還可以把jar/war變?yōu)橐粋€(gè)可以執(zhí)行的腳本來啟動,比如./demo.jar,這篇文章主要介紹了spring?boot?executable?jar/war?原理,需要的朋友可以參考下2023-02-02

