关于spring 注入中循环 ,环形依赖问题的明确

本文通过实验探讨了Spring框架中不同情况下Bean之间的循环依赖问题。分别分析了构造方法注入与Setter方法注入、Singleton与Prototype作用域下的循环依赖处理机制。

首先为什么叫明确了?就是因为好多人对于这个到底可以不可以是很模糊的,我也询问过好多人,都说是不可以的,但是还有一部分人认为是可以的。那么为什么会出现这2种截然不同的方式?初步判断使用方式不同导致的,。最重要回过头了再说这个问题最重要的原因是之前自己了解过,但是不是很明确,像大家一样还是模糊的认为可以或者不可以,还有另外一个很重要的原因,在面试中面试官提到了这个问题,但是面试官也是果断的说是不可以。导致我很纠结,一定要名曲而这个问题。



首先来构建测试的场景

1.通过2个类相互持有对方,通过构造方法的方式注入,都是默认singleton 代码如下

package com.slj.test;


import org.springframework.stereotype.Component;


@Component
public class CircleA {
private CircleB circleB;


public CircleA() {
System.out.println("constrctor of A");
}



public CircleA(CircleB circleB) {
System.out.println("constrctor of A a B="+circleB);
this.circleB = circleB;
}

}

package com.slj.test;


import org.springframework.stereotype.Component;


@Component
public class CircleB {

public CircleB() {
System.out.println("constrctor of B");
}


public CircleB(CircleA circleA) {
System.out.println("constrctor of B A="+circleA);
this.circleA = circleA;
}


private CircleA circleA;

}

配置配置文件

        <bean id="circleA" class="com.slj.test.CircleA" >
<constructor-arg index="0" ref="circleB"> </constructor-arg>
</bean>
<bean id="circleB" class="com.slj.test.CircleB" >
<constructor-arg index="0" ref="circleA"> </constructor-arg>
</bean>

建立测试类

package com.slj.main;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


import com.slj.test.CircleA;


public class TestCircle {


public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CircleA circleA = (CircleA) ac.getBean("circleA");
        System.out.println(circleA.getCircleB());
}


}

,然后运行结果果断报错,和那个面试官的观点一样

Error creating bean with name 'circleA': Requested bean is currently in creation: Is there an unresolvable circular reference?


2.通过2个类相互持有对方,通过Setter的方式注入,都是默认singleton 代码做适当的调整就可以



但是这次的结果是可以,而且成功注入了。是不是不可思议?后面我会讨论具体原因。




3..通过2个类相互持有对方,通过Setter的方式注入,都是prototype代码做适当的调整就可以

运行结果报错:

Error creating bean with name 'circleA': Requested bean is currently in creation: Is there an unresolvable circular reference?


4.通过2个类相互持有对方,通过构造方法的方式注入,都是prototype代码做适当的调整就可以

运行结果报错:

Error creating bean with name 'circleA': Requested bean is currently in creation: Is there an unresolvable circular reference?




那么我们定下心来分析分析具体原因吧。


1.首先对于第一种情况,通过构造方法来构造,但是bean池中的对方bean 还没有初始完,所以灭有暴漏出来。故此构造不成功报错。

2.对于第二种情况,首先通过空构造方法来实例化,实例化后,bean都暴漏出来了。然后通过setter方法来完整这个bean 。

3.对于首先通过空构造方法来实例化,实例化后,是非单例的,

具体的单例非单例的情况还需要继续讨论spring 对于bean的管理。

其中可能有说的不对的地方,希望大家指正,如有帮助请抠1.

### Spring 框架中的环形依赖解决方案及其工作原理 #### 理解环形依赖的概念 当两个或多个bean相互之间存在直接或间接的依赖关系,就会形成环形依赖。例如A依赖于B,而B又反过来依赖于A。这种情况下如果不加以处理,则可能导致无限递归调用最终引发栈溢出错误。 #### Spring 处理环形依赖的工作机制 对于单例作用域内的bean,在默认情况下Spring容器能够自动检测并尝试解决简单的环形单例bean之间的依赖问题。具体来说: - 当遇到第一个被请求创建的对象(比如`Bean A`),如果发现它有未满足的属性注入需求指向另一个尚未完成初始化过程的目标对象(`Bean B`); - 此刻并不会立即去真正构建那个目标对象而是先返回给前者一个早期暴露出来的半成品版本——即提前曝光(early exposure)状态下的代理实例; - 接着继续按照正常的流程来组装剩余部分直至整个生命周期结束得到完整的`Bean A`; - 同样的逻辑也适用于后续其他涉及相同链条上的组件直到全部装配完毕为止[^2]。 #### 提前曝光与三级缓存机制 为了实现上述功能,Spring内部维护了一个所谓的“三级缓存”,其中就包含了用于应对可能出现的循环引用情况的数据结构: 1. **一级缓存** (`singletonObjects`) :存储已经完全初始化好的单例bean。 2. **二级缓存** (`earlySingletonObjects`) : 存放处于正在创建过程中但是可以安全使用的临占位符(proxy placeholder),也就是前面提到过的那种预发布版/半成品形态; 3. **三级缓存**( `singletonFactories`):记录了从工厂方法获取到实际对象之前的中间产物,以便将来可能需要用到的候可以直接转换成真正的实体而不必重新走一遍全流程[^4]。 一旦某个bean成功完成了自己的构造函数执行以及所有前置处理器(post-processors)的操作之后,便会正式加入到最上层的一级缓存里供外部访问使用;与此同也会清理掉对应位置处存在于另外两层缓冲区里的残留数据项以防混淆视听造成不必要的麻烦。 #### 如何预防和解决问题 尽管Spring提供了一套相对完善的内置机制用来缓解大部分常规场景下所面临的挑战,但在某些特殊情形中仍然可能会碰到无法绕过的技术壁垒。此就需要开发者采取额外措施来进行干预调整,常见的做法包括但不限于改变bean的作用范围(scope),利用懒加载(lazy initialization)特性延迟初始化间点,或是重构代码减少不必要的双向关联程度等等。 ```java // 示例:通过设置lazy-init="true" 属性开启懒加载模式 <bean id="exampleBean" class="com.example.ExampleClass" lazy-init="true"/> ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值