如果使用构造函数注入,则可能会创建一个无法解析的循环依赖场景。
下面详细谈谈循环以来是如何出现的,又如何避免这种情况发生。
什么是循环依赖 下面是我所遇到的情况,代码结构如下: SpringSecurity 配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Configuration public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { private final 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()); @Autowired public MyUserDetailService (PasswordEncoder passwordEncoder) { this .passwordEncoder = passwordEncoder; } ... ... }
运行之后,Spring抛出了如下错误信息:
1 2 3 4 5 6 7 8 9 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方法的依赖注射 取代基于构造函数的依赖注入来解决循环依赖。