什么是循环依赖?怎么解决循环依赖?为什么一定用三级缓存?

什么是循环依赖?

多个实体之间相互依赖形成闭环的情况叫做“循环依赖”,也叫做“循环引用”

Spring可以解决哪些情况的循环依赖呢?

看以下几种情景:

依赖情况依赖注入方式是否支持
AB循环依赖AB均采用构造器注入
AB循环依赖AB均采用setter方法注入
AB循环依赖AB均采用属性自动注入
AB循环依赖A中注入的B为setter注入,B中注入的A为构造器注入
AB循环依赖B中注入的A为setter注入,A中注入的B为构造器注入

如代码:

<bean id="userService" class="com.it.service.impl.UserServiceImpl">
   <property name="UserDao" ref="UserDao"/>
</bean>
<bean id="userDao" class="com.it.dao.impl.UserDaoImpl">
   <property name="userService" ref="userService"/>
</bean>

看这个时候的创建过程

这时userService想要初始化需要userDao,而userDao又需要userService。这时就会陷入死循环。

那么Spring是怎么解决循环依赖的呢?

答案:三级缓存

Spring提供了三级缓存存储完整的Bean实例和半成品Bean实例,用于解决循环依赖问题。

一级缓存:是一个名为 singletonObjects 的 ConcurrentHashMap。singletonObjects是单例池,用于保存实例化,属性赋值,初始化完成的bean实例。

二级缓存:是一个名为 earlySingletonObjects 的 ConcurrentHashMap,用于保存半成品Bean实例,是实例化完成的Bean实例,且当前对象已经被其他对象引用了。

三级缓存:是一个名为 singletonFactories 的 ConcurrentHashMap,用于存储半成品对象,对象未被引用,使用时通过工厂创建Bean。

下面我们结合这个例子来看三级缓存解决循环依赖的过程

<bean id="userService" class="com.it.service.impl.UserServiceImpl">
   <property name="UserDao" ref="UserDao"/>
</bean>
<bean id="userDao" class="com.it.dao.impl.UserDaoImpl">
   <property name="userService" ref="userService"/>
</bean>

  1. 1、UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
  2. 2、UserService属性注入,需要UserDao, 从缓存中获取,没有UserDao;
  3. 3、UserDao实例化对象, 但尚未初始化,将UserDao存储到到三级缓存;
  4. 4、UserDao属性注入,需要UserService, 从三级缓存获取UserService, UserService从三级缓存移入二级缓存;
  5. 5、UserDao执行其他生命周期过程, 最终成为一个完成Bean,存储到一级缓存,删除二、三级缓存;
  6. 6、UserService注入UserDao;
  7. 7、UserService执行其他生命周期过程, 最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

为什么必须是三级缓存?二级缓存可以吗?

不行。

当某个 bean 进入到 2 级缓存的时候,说明这个 bean 的早期对象被其他 bean 注入了,就是说,这个 bean 还是半成品,还未完全创建好的时候,已经被别人拿去使用了,所以必须要有 3 级缓存,2 级缓存中存放的是早期的被别人使用的对象,如果没有 2 级缓存,是无法判断这个对象在创建的过程中,是否被别人拿去使用了。

如果是没有代理的情况下,使用⼆级缓存解决循环依赖也是 OK 的。但是如果存在代理,三级没有问题,二级就不行了。 因为三级缓存中放的是生成具体对象的匿名内部类,获取 Object 的时候,它可以生成代理对象,也可以返回普通对象。使用三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。 假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的 Bean 对象,Bean 初始化过程中,通过 BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通 Bean 对象,那么可能就导致取 到的 Bean 对象不⼀致了。

三级缓存是为了判断循环依赖的时候,早期暴露出去已经被别人使用的 bean 和最终的 bean 是否是同一个 bean,如果不是同一个则弹出异常,如果早期的对象没有被其他 bean 使用,而后期被修改了,不会产生异常,如果没有三级缓存,是无法判断是否有循环依赖,且早期的 bean 被循环依赖中的 bean 使用了。

  • 70
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值