Spring循环依赖的解决方案——三级缓存

要解决循环依赖,就要先了解Bean的声明周期

Bean

定义:被IOC容器管理的对象

依赖注入方式

  1. 构造器注入

    优点:可以注入一个final修饰的对象;只会执行一次,不会被篡改;依赖的对象是完全初始化的

  2. Setter注入

    缺点:无法注入一个final修饰的对象;容易被篡改

  3. 注解注入

    缺点:无法注入一个final修饰的对象;只适用于IOC容器

生命周期

  1. 实例化
  2. 属性赋值:set一些属性值
  3. 初始化:执行一些aware接口中的方法、initMethod`方法、前置处理、后置处理等方法。
  4. 销毁

在这里插入图片描述

Spring循环依赖

Spring解决循环依赖的关键是提前暴露未完全创建的Bean

三级缓存

  • 一级缓存:存储完全初始化的Bean
  • 二级缓存:存储动态代理(完成实例化,但未进行属性注入和初始化的Bean),防止多重循环依赖多次创建动态代理
  • 三级缓存:存储函数式接口(把Bean实例和名字放到方法中)
    • 不会立马调用
    • 实现了Aop则创建动态代理,没有实现则返回Bean实例,存储到二级缓存中

不使用二级缓存:如果只有二级缓存,所有的Bean不管是否循环依赖,都会在实例化时创建动态代理,而正常的Bean是在初始化后才创建动态代理的,违背了Bean的生命周期。(破坏循环依赖,其实使用二级缓存就够了,但为了不违背Bean生命周期,就使用三级缓存)

例:A—>B B—>A

从容器中获取A对象,没有则创建Bean;

创建Bean时,在实例化阶段,会先从一级缓存中获取完全初始化的Bean,如果获取不到的话,再从二级缓存中获取,再获取不到,则实例化Bean,并将Bean对象和名称放到函数式接口中,存储到三级缓存中;

属性注入阶段,因为A依赖B,B不存在,需要创建,同上,在三级缓存中存储函数式接口。又因为B依赖A,依次判断各级缓存中是否存在,最后创建实例的代理对象存储到二级缓存中;

初始化完毕后,B对象创建完成,删除二级三级缓存,将Bean存储到一级缓存;

A属性注入完成后,再初始化。

在这里插入图片描述

Spring的三级缓存已经解决了循环依赖的问题,但当遇到以下情况时,还是会报错

  1. 全部使用构造器注入
  2. 自己注入自己

解决方案

  1. 重新设计

    产生循环依赖的原因可能是没有把各个类的职能区分开。

  2. Setter注入

    全部使用构造器注入,在创建对象的时候就要求创建依赖对象了,而依赖对象又不能初始化。故可以一个使用setter注入,一个使用构造器注入

  3. @Lazy延迟加载

    在构造方法上加上@Lazy。构造A的时候,不会创建B,而是注入一个B的代理对象,B未被真正的初始化。等到A实例调用B的方法时,代理对象才会触发B真正初始化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值