多个Bean之间互相依赖并形成一个闭环叫做循环依赖。Bean的依赖注入分为构造器注入和Setter注入两种方式。
在采用构造器注入的方式配置Bean时,很可能会产生循环依赖的情况,Spring启动时会抛出BeanCurrentlyInCreationException异常。如果采用构造器注入方式,解决循环依赖的手段是打破循环,可以让Spring懒加载其中一个Bean(使用@Lazy注解),不完全初始化Bean,创建一个代理然后将它注入另一个Bean,注入的Bean在第一次需要时才完全创建它,demo如下:
@Component
public class CircularDependencyA {
private CircularDependencyB circB;
@Autowired
public CircularDependencyA(@Lazy CircularDependencyB circB) {
this.circB = circB;
}
}
使用setter方法注入的方式。 除了极少数例外,大部分的循环依赖都是可以避免的。 Spring的Autowired注解也是通过setter方式注入的。使用setter方式注入是在Bean构造(初始化)之后进行的,Spring会使用三级缓存来解决循环依赖。
三级缓存解决循环依赖
案例介绍:
lagouBean有一个属性引用itBean,itBean也有一个属性引用lagouBean,然后有一个日志的横切逻辑增强lagouBean。lagouBean与itBean形成循环依赖。
容器在启动时处理流程如下:
1.实例化lagouBean,创建lagouBeanFactory放入三级缓存;
2.lagouBean依赖属性itBean,所以需要实例化itBean,创建itBeanFactory放入三级缓存;
3.由于itBean依赖lagouBean,所以用三级缓存中的lagouBeanFactory创建一个lagouBean的代理对象放入二级缓存并引用,并移除三级缓存中的lagouBeanFactory;
4.此时itBean创建完成,将itBean放入一级缓存,并移除三级缓存中的itBeanFactory;
5.itBean实例化完成后会继续实例化lagouBean,从二级缓存中找到lagouBean继续装配;
6.lagouBean装配上一级缓存中的itBean,完成实例化放入一级缓存中,两个Bean都创建完毕;
实例化过程如图:
一级缓存是单例池,存放完整的SpringBean。二级缓存存放早期单例对象。三级缓存存放单例工厂对象。三级缓存的主要作用就是产生代理对象。此案例之所以需要一个日志的横切逻辑是为了说明三级缓存的作用。如果没有横切增强时,使用二级缓存就能处理循环依赖。