【Spring面试题】循环依赖如何解决?

1.什么是循环依赖

循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环。

2.循环依赖的两种情况及其解决方案

2.1 构造器的循环依赖

这种依赖 spring 本身是处理不了的

在这种情况下 Spring 将在加载上下文时引发 BeanCurrentlyInCreationException 异常,因为 Spring 无法决定应该先创建哪个 bean,因为它们彼此相互依赖。

当我们使用构造方法进行注入时,才会遇到这种情况,因为它在上下文加载时就被要求注入。setter 和 field 方法注入则不会发生循环依赖,因为它们不会在创建 Bean 时就注入依赖,而是在被需要时才注入。

解决方案:

  • 采用 setter 和 field 方法注入
  • 使用懒加载 @Lazy
  • 使用 @PostConstruct
  • 实现 ApplicationContextAware 和 InitializingBean 接口

2.2 单例模式下的 setter 循环依赖

Spring 解决循环依赖的机制是根据 Spring 框架内定义的三级缓存来实现的,三级缓存解决了 Bean 之间的循环依赖问题。

从源码中来看看 Spring 中 Bean 工厂是怎么获取 Bean 的(AbstractBeanFactory中):

一级一级向下寻找,可以找到 DefaultSingletonBeanRegistry 类中的三级缓存,其实就是三个 Map 集合类:

  • 第一级缓存【singletonObjects】:里面存放的是实例化好和完成初始化的单例对象
  • 第二级缓存【earlySingletonObjects】:里面存放的是提前曝光的单例对象,这个 Bean 实例化了,但是还没有初始化
  • 第三级缓存【singletonFactories】:里面存放的是要被实例化的对象的对象工厂。

所以当一个 Bean 调用构造函数进行实例化后,即使这时候属性还未填充,依然可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于 Java 的引用传递),这也说明了另外一点,基于构造器的注入,如果有循环依赖,Spring是不能够解决的。

还有一点,Spring 默认的 Bean Scope 是单例的,而三级缓存中都包含 singleton,可见是对于单例 Bean 之间的循环依赖的解决。

其实二级缓存也是可以解决循环依赖的。为什么 Spring 不选择二级缓存?

如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。所以,Spring 选择了三级缓存。但是因为循环依赖的出现,导致了 Spring 不得不提前去创建代理,因为如果不提前创建代理对象,那么注入的就是原始对象,这样就会产生错误。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
面试高级开发的期间整理的面试题目,记录我面试遇到过的spring题目以及答案 目录 spring ThreadLocal的底层对象; 为什么@Service和@Repository放到实现类上面而不是接口类上面; spring 三种注入(就是从spring容器中将bean放入对象属性值中) Spring下描述依赖关系@Resource, @Autowired和@Inject的区别与联系 Spring中BeanFactory和ApplicationContext的区别 谈谈Spring IOC的理解,原理与实现? bean的生命周期,详细看上面 SpringBoot自动装配的过程的原理: spring的缓存; spring是如何解决循环依赖; BeanFactory和FactoryBean有什么区别; Spring中用到的设计模式; SPI 机制(Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制), 很多地方有用到: AOP Spring的AOP的底层实现原理; 为什么jdk动态代理是必须是接口 两种动态代理的区别 AOP实现方式:aop注解或者xml配置;后来工具jar包aspects; aop的属性 事务 事务编码方式: 事务注意事项; 为什么同一个类A调用b方法事务,A方法一定要有事务(编码式的不用) @transaction多个数据源事务怎么指定数据源 传播特性有几种?7种; 某一个事务嵌套另一个事务的时候怎么办? REQUIRED_NEW和REQUIRED区别 Spring的事务是如何回滚的,实现原理; 抽象类和接口的区别,什么时候用抽象类什么时候用接口; StringBuilder和StringBuffer的区别 java值传递和引用传递

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的架狗师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值