如果使用構(gòu)造函數(shù)注入,則可能會(huì)創(chuàng)建一個(gè)無(wú)法解析的循環(huán)依賴(lài)場(chǎng)景。
什么是循環(huán)依賴(lài)
循環(huán)依賴(lài)其實(shí)就是循環(huán)引用,也就是兩個(gè)或則兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán)。比如A依賴(lài)于B,B依賴(lài)于C,C又依賴(lài)于A。如下圖:
注意,這里不是函數(shù)的循環(huán)調(diào)用,是對(duì)象的相互依賴(lài)關(guān)系。循環(huán)調(diào)用其實(shí)就是一個(gè)死循環(huán),除非有終結(jié)條件。
Spring中循環(huán)依賴(lài)場(chǎng)景有:
(1)構(gòu)造器的循環(huán)依賴(lài)
(2)field屬性的循環(huán)依賴(lài)。
怎么檢測(cè)是否存在循環(huán)依賴(lài)
檢測(cè)循環(huán)依賴(lài)相對(duì)比較容易,Bean在創(chuàng)建的時(shí)候可以給該Bean打標(biāo),如果遞歸調(diào)用回來(lái)發(fā)現(xiàn)正在創(chuàng)建中的話(huà),即說(shuō)明了循環(huán)依賴(lài)了。
下面是我所遇到的情況,代碼結(jié)構(gòu)如下:
SpringSecurity 配置類(lèi):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { private final UserDetailsService userDetailsService; /** * 通過(guò)配置類(lèi)構(gòu)造函數(shù)注入 UserDetailsService */ @Autowired public BrowserSecurityConfig(UserDetailsService userDetailsService) { this .userDetailsService = userDetailsService; } /** * 在配置類(lèi)中聲明 加密編碼器 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } ... ... } |
UserDetailsService 類(lèi):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Component public class MyUserDetailService implements UserDetailsService { private final PasswordEncoder passwordEncoder; private Logger logger = LoggerFactory.getLogger(getClass()); /** * 通過(guò)構(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 通過(guò)構(gòu)造函數(shù)注入 UserDetailsService實(shí)例,而 UserDetailsService由通過(guò)構(gòu)造函數(shù)注入在BrowserSecurityConfig 中聲明的PasswordEncoder。
總結(jié)來(lái)說(shuō),Spring Bean的循環(huán)依賴(lài)是指,類(lèi)A需要通過(guò)構(gòu)造函數(shù)注入的類(lèi)B的實(shí)例(或者B中聲明的Bean),而類(lèi)B需要通過(guò)構(gòu)造函數(shù)注入的類(lèi)A的實(shí)例(或者A中聲明的Bean)。如果將類(lèi)A和類(lèi)B的bean配置為相互注入,則Spring IoC容器會(huì)在運(yùn)行時(shí)檢測(cè)到此循環(huán)引用,并引發(fā)一個(gè)BeanCurrentlyInCreationException。與典型情況(沒(méi)有循環(huán)依賴(lài))不同,bean A和bean B之間的循環(huán)依賴(lài)關(guān)系迫使其中一個(gè)bean在被完全初始化之前被注入到另一個(gè)bean中(這是一個(gè)典型的“先有雞還是先有蛋”場(chǎng)景)。
解決方案
簡(jiǎn)明扼要的說(shuō),就是——不使用基于構(gòu)造函數(shù)的依賴(lài)注入。可通過(guò)下面方式解決。
在字段上使用@Autowired注解,讓Spring決定在合適的時(shí)機(jī)注入。【推薦】
用基于setter方法的依賴(lài)注射取代基于構(gòu)造函數(shù)的依賴(lài)注入來(lái)解決循環(huán)依賴(lài)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://www.jianshu.com/p/d935341694d2