Spring循环依赖难道只能通过两个Setter注入解决嘛?

本文探讨了Spring中循环依赖问题的解决方案,指出构造器注入与Setter注入结合能有效避免报错,关键在于控制依赖加载顺序。

Spring循环依赖难道只能通过两个Setter注入解决嘛?

先说结论:并不是,可以是一个setter和一个构造器

如果存在循环依赖的对象关系的话,位置靠上的类会先进行生命周期的加载

所以位置靠上的类必须通过set注入其他属性,这样才不会导致循环依赖的报错

前言

其实想到这个问题是由于网上给出的结论是

于是我便好奇了起来:为什么同样是一个通过Setter注入、一个通过构造器注入,会有两种不同的结果呢?

Setter注入与构造器注入

首先我们先明确哪种是setter注入,哪种是构造器注入

//setter注入
@Autowired
private B b;

//构造器注入
@Autowired
public B(A a) {
    this.a = a;
}

为什么可以一个通过set和一个通过构造器进行注入也不会导致循环依赖的报错

Spring默认在package从上至下加载Bean的生命周期:创建->注入->初始化完成
我们假设代码中,A所在位置在B上方

先构造器注入,再set注入

条件:A是构造器注入,B是set注入

由于A在上方那么会先进行A的生命周期

A在构造器创建实例时,由于容器中B是空的,导致A无法通过构造器创建对象,于是报错

即使为A添加了空参构造器也无济于事,因为使用构造器注入会先行调用有参构造器

先set注入,再构造器注入

条件:A是set注入,B是构造器注入

由于A在上方还是会先进行A的生命周期

此时,由于A有默认的无参构造器直接创建一个空对象放入了三级缓存,为其注入B时,发现容器中B是空的

于是开始B的生命周期,因为在三级缓存中A已经存在,所以B可以直接根据这个对象去通过构造器实例化

实际上是会因为引用了A,为A执行一个提前引用来创建一个动态代理对象放入二级缓存中,然后将二级缓存中A的代理对象给B,这里为了方便理解直接忽略了动态代理这一层缓存

于是B被创建、注入、初始化完成后加入到一级缓存,此时又来到了A的注入阶段,直接将B注入

于是A经过注入与初始化后,也加入到了IOC容器中,至此不会导致报错

所以可以得出结论:并不是只能通过两个Setter注入才能解决循环依赖的问题,也可以是一个setter和一个构造器

如果存在循环依赖的对象关系的话,位置靠上的类会先进行生命周期的加载

所以位置靠上的类必须通过set注入其他属性,这样才不会导致循环依赖的报错

结论依据:

  • 可以通过实现BeanPostProcessor前置调用类的方法将所有Bean依次输出
  • 也可以通过实现BeanNameAware的方法输出Bean的名称

总之我们会发现是B先被初始化完成后才初始化完成了A
所以是先创建了A,然后为其注入B时,启动了B的完整的一个生命周期,才会是B先被初始化完成的这样一个结果

<think>好的,我现在需要回答用户关于Spring框架中使用setter注入是否能解决循环依赖的问题。首先,我要回忆一下Spring的依赖注入机制,尤其是构造器注入setter注入的区别。 用户提到的引用[3]指出,循环依赖发生在两个或多个bean通过构造器相互注入。这时候Spring无法解决,因为构造器注入在实例化bean时必须立即注入依赖,导致循环无法打破。而引用[1]提到,使用setter注入可能是一个解决办法,因为它允许在对象实例化后设置依赖,从而避免了构造器注入的立即性要求。 接下来,我需要理解Spring的三级缓存机制。引用[2]提到,三级缓存(singletonFactories)用于处理存在动态代理的情况。当bean还在创建过程中时,Spring会将未完成初始化的bean提前暴露到缓存中,这样其他bean可以引用这个半成品,之后再通过setter完成依赖注入。这个过程允许循环依赖setter注入的情况下被解决,因为依赖的设置可以延后到实例化之后。 然后,我需要验证这个机制是否真的适用于setter注入。根据Spring官方文档,当使用setter注入时,Spring先实例化bean,然后在后续步骤中通过setter方法注入依赖。因此,如果两个bean都使用setter注入Spring可以先将它们的实例创建出来(此时属性尚未设置),然后将这些半成品bean放入缓存,之后在注入阶段再互相设置依赖。这样就避免了构造器注入时的循环问题。 另外,用户提供的引用[1]建议使用setter注入来配置循环依赖,虽然不推荐,但技术上可行。这说明虽然Spring不鼓励循环依赖,但在某些情况下,setter注入确实能绕过构造器注入的限制。 总结一下,用户的问题核心是确认setter注入是否能解决循环依赖。根据引用和Spring机制,答案应该是肯定的,但需要明确其限制和潜在问题,比如可能隐藏设计缺陷,增加维护难度等。因此,在回答时需要说明setter注入如何通过延迟依赖注入解决循环问题,同时提醒用户应优先考虑代码结构优化。</think>在Spring框架中,**通过setter注入可以解决部分循环依赖问题**,但需结合Spring的三级缓存机制实现。以下是具体分析: --- ### 一、循环依赖的产生条件 当两个Bean通过**构造器注入**相互依赖时,Spring无法解决循环依赖[^3]。例如: ```java // BeanA通过构造器注入BeanB public class BeanA { private BeanB b; public BeanA(BeanB b) { this.b = b; } } // BeanB通过构造器注入BeanA public class BeanB { private BeanA a; public BeanB(BeanA a) { this.a = a; } } ``` 此时Spring会直接抛出`BeanCurrentlyInCreationException`。 --- ### 二、setter注入解决机制 若改用**setter注入**,Spring通过以下步骤实现依赖解析: 1. **提前暴露半成品对象** Spring在实例化Bean后(但未完成属性注入)时,将对象存入三级缓存`singletonFactories`[^2]。例如: ```java // BeanA通过setter注入BeanB public class BeanA { private BeanB b; public void setB(BeanB b) { this.b = b; } } ``` 2. **分阶段完成依赖注入** - 先创建`BeanA`实例(此时`BeanB`属性为空) - 将`BeanA`的引用存入缓存 - 创建`BeanB`实例时,从缓存获取`BeanA`的引用并注入 - 最后通过setter完成`BeanA`对`BeanB`的注入 --- ### 三、实现条件与限制 | 方案 | 可行性 | 限制条件 | |--------------------|--------|----------------------------------| | 纯构造器注入 | ❌ | 必然失败 | | setter注入 | ✅ | 需配合Spring三级缓存机制[^2] | | 字段注入(@Autowired)| ✅ | 底层实现与setter注入类似 | --- ### 四、潜在风险与建议 1. **设计缺陷的掩盖** 循环依赖通常意味着代码存在职责划分问题,强行解决可能降低可维护性。 2. **动态代理的兼容性** 若Bean需要AOP动态代理,必须通过`@Lazy`或接口代理确保代理对象正确生成[^2]。 3. **官方建议** Spring官方文档明确指出应尽量避免循环依赖,重构代码结构是更优解[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值