面试题答案
一键面试循环依赖产生原因
在Spring Boot的依赖注入过程中,当两个或多个bean相互依赖,形成一个闭合的依赖环时,就会产生循环依赖。例如,Bean A依赖Bean B,而Bean B又依赖Bean A。在创建Bean A时,需要先实例化Bean B,但在实例化Bean B的过程中,又需要实例化Bean A,这样就陷入了死循环。
解决方法及原理
- 使用构造器注入结合
@Lazy
注解- 方法:在构造器注入时,对其中一方使用
@Lazy
注解。 - 原理:
@Lazy
注解会使被依赖的bean以代理的方式进行注入,延迟初始化。这样在初始化依赖环中的第一个bean时,不会立即尝试初始化其依赖的bean,而是在真正使用该依赖时才进行初始化,从而打破循环依赖。例如,假设Bean A构造器依赖Bean B,给Bean B的注入点添加@Lazy
注解,在实例化Bean A时,不会马上实例化Bean B,而是创建一个代理对象,当Bean A真正使用到Bean B时,才去实例化Bean B。
- 方法:在构造器注入时,对其中一方使用
- 使用
@Autowired
的setter方法注入- 方法:将构造器注入改为使用
@Autowired
注解的setter方法注入。 - 原理:Spring容器在实例化bean时,会先实例化一个原始的bean对象(此时bean的属性还未填充),并将其提前暴露到
singletonFactories
缓存中。当其他bean需要依赖这个bean时,可以从缓存中获取到这个早期的bean引用,虽然此时其属性可能还未完全初始化,但不会导致循环依赖。例如,Bean A通过setter方法依赖Bean B,Bean B通过setter方法依赖Bean A。Spring先实例化Bean A并放入早期对象缓存,然后实例化Bean B,在填充Bean B的属性时,发现依赖Bean A,就从缓存中获取Bean A的早期引用,填充到Bean B中,接着继续填充Bean A的属性,这样就避免了循环依赖。
- 方法:将构造器注入改为使用