怎么解决Spring的循环依赖


前言

首先要知道Spring的依赖注入也就是DI,分为setter注入和构造器注入。但是Spring能够解决的是setter注入,构造器注入是不能够解决的。回到Spring的单例模式和多例模式下,多例模式下的依赖注入也是解决不掉的。所以只关注setter注入的循环依赖解决。其次,Spring的生命周期分为:实例化、属性注入、初始化和销毁。


一、什么是循环依赖?

 @Component
    public class A{
        @Autowired
        B b;
    }
    @Component
    public class B{
        @Autowired
        A a;
    }

我们定义这样两个类一个是A,一个是B。但是A中有一个属性引用的B,B中的一个属性引用了A,这就是一个典型的循环依赖,形成一个圈,也可以是多个bean循环依赖,比如A依赖B,B依赖C,C依赖A。实例化A的时候,我们去属性赋值B,B也去实例化,B属性赋值A,然后A再去找B,于是产生了死循环。这就是循环依赖问题。

二、三级缓存机制

    /** 一级缓存,用于储存完全初始化好的bean  **/
    private final Map<String,Object> singletonObject = new ConcurrentHashMap<String, Object>(256);

    /** 二级缓存,存放原始的bean对象(属性尚未填充),用于解决循环依赖 **/
    private final Map<String,Object> earlySingletonObject = new HashMap<String, Object>(16);

    /** 三级缓存,存放bean工厂对象,用于解决循环依赖问题 **/
    private  final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

如果只有一级缓存,那会怎样?
首先A对象进行实例化,A要进行属性填充B。但B还没有创建,于是开始B进行实例化,同样B也要进行属性填充,发现它也需要A。然而发现一级缓存中还没有A,然后又创建A,于是就产生了死循环。循环往复,直到栈溢出。那么如果直接将A放入一次缓存,会产生什么后果呢?如果直接把A放入map中,map中的A就是个假A,缺胳膊少腿的,当真正使用这个A的时候,就会出现空指针异常。而且我们规定一级缓存只能存放完全初始化好的bean。

我们知道一级缓存无法储存 半成品对象,也就是没有完全初始化好的对象,所以就引入了二级缓存,来储存这个东西。专门储存开始创建但是不完整的对象。我们把一级缓存叫map1,二级缓存叫map2。首先我们还是从实例化A开始,我们实例化后,还没有进行属性填充的时候,就把A对象放入map2备用。然后进行属性填充,A去填充B,发现B没有实例化,于是B同样实例化,把自己的半成品放入map2中。B开始进行填充,发现map1中没有A,再去map2中去寻找,发现map2中有。于是B直接从map2中拿到A使自己变得完整。这时候B就会把自己放入map1中,并把map2中的半成品删除了。回到A的阶段,A发现map1中有B了,那么A也完成了属性创建。于是双方都完成了自己的创建。这就是二级缓存解决的问题。

那么这么来说,二级缓存就可以解决循环依赖问题,那问什么要搞出来一个三级缓存呢?
主要是因为Spring的Aop机制所产生的代理对象问题。
首先要了解Spring的代理对象产生阶段是在填充属性完成后才进行的,原理通过后置处理器BeanPostProcessor来实现。如果说我们如果用二级缓存来解决,那么就要在属性填充的时候就要将代理对象生成好,放入二级缓存。那这就与Spring的对象生命周期相反。所以就要引入三级缓存。
首先我们还是先实例化A,这个时候就把A的ObjectFactory对象放入map3中。同样进行属性填充,这样B还没有创建,于是就去创建B。于是就去实例化B,那么在实例化B的过程中,我们也一样,先把B的ObjectFactory放入map3中。再把B对象放入map2中。继续执行B的属性填充,去获取A的对象,此时map1中没有A,因为A还没有创建,但是我们发现map3中有A的ObjectFactory对象,那么我们通过ObjectFactory对象获得A的早期对象,然后将这个早期对象放入map2中,同时删除了map3中的A,然后我们将A的引用给了B,那么这个B就是完整的了,就把B放入map1中同时删除map3中的B。这时候B已经创建完成了,A继续执行B的属性填充可以拿到B对象,这样A也可以完成创建,最后我们把A对象也放入map1,删除map2中的A,这样循环依赖就可以解决了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值