如果使用構造函數注入,則可能會創建一個無法解析的循環依賴場景。
什么是循環依賴
循環依賴其實就是循環引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉環。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:
注意,這里不是函數的循環調用,是對象的相互依賴關系。循環調用其實就是一個死循環,除非有終結條件。
Spring中循環依賴場景有:
(1)構造器的循環依賴
(2)field屬性的循環依賴。
怎么檢測是否存在循環依賴
檢測循環依賴相對比較容易,Bean在創建的時候可以給該Bean打標,如果遞歸調用回來發現正在創建中的話,即說明了循環依賴了。
下面是我所遇到的情況,代碼結構如下:
SpringSecurity 配置類:
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; /** * 通過配置類構造函數注入 UserDetailsService */ @Autowired public BrowserSecurityConfig(UserDetailsService userDetailsService) { this .userDetailsService = userDetailsService; } /** * 在配置類中聲明 加密編碼器 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } ... ... } |
UserDetailsService 類:
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()); /** * 通過構造函數注入 PasswordEncoder */ @Autowired public MyUserDetailService(PasswordEncoder passwordEncoder) { this .passwordEncoder = passwordEncoder; } ... ... } |
運行之后,Spring拋出了如下錯誤信息:
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 通過構造函數注入 UserDetailsService實例,而 UserDetailsService由通過構造函數注入在BrowserSecurityConfig 中聲明的PasswordEncoder。
總結來說,Spring Bean的循環依賴是指,類A需要通過構造函數注入的類B的實例(或者B中聲明的Bean),而類B需要通過構造函數注入的類A的實例(或者A中聲明的Bean)。如果將類A和類B的bean配置為相互注入,則Spring IoC容器會在運行時檢測到此循環引用,并引發一個BeanCurrentlyInCreationException。與典型情況(沒有循環依賴)不同,bean A和bean B之間的循環依賴關系迫使其中一個bean在被完全初始化之前被注入到另一個bean中(這是一個典型的“先有雞還是先有蛋”場景)。
解決方案
簡明扼要的說,就是——不使用基于構造函數的依賴注入。可通過下面方式解決。
在字段上使用@Autowired注解,讓Spring決定在合適的時機注入。【推薦】
用基于setter方法的依賴注射取代基于構造函數的依賴注入來解決循環依賴。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://www.jianshu.com/p/d935341694d2