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

什么是循环依赖?

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

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 使用了。

相关推荐

  1. sping怎么解决循环依赖

    2024-05-04 13:48:02       32 阅读
  2. Spring 怎么解决循环依赖的问题?

    2024-05-04 13:48:02       29 阅读
  3. Spring依赖注入、循环依赖——三级缓存

    2024-05-04 13:48:02       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-04 13:48:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-04 13:48:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-04 13:48:02       82 阅读
  4. Python语言-面向对象

    2024-05-04 13:48:02       91 阅读

热门阅读

  1. 输入序列太长 gan CGAN

    2024-05-04 13:48:02       27 阅读
  2. Spring Bean Scope

    2024-05-04 13:48:02       37 阅读
  3. 网络工程师----第十九天:

    2024-05-04 13:48:02       36 阅读
  4. Python爬虫:线程,进程与协程

    2024-05-04 13:48:02       33 阅读
  5. HttpUtil的定义

    2024-05-04 13:48:02       29 阅读
  6. 【代码随想录】day50

    2024-05-04 13:48:02       32 阅读
  7. Redis rehash 相关问题

    2024-05-04 13:48:02       35 阅读
  8. File 文件搜索,啤酒问题,删除非空文件夹

    2024-05-04 13:48:02       32 阅读
  9. db2常用命令大全(高级篇)

    2024-05-04 13:48:02       31 阅读