Spring中的循环依赖问题及解决方案

一、循环依赖的概念

1.概念

        循环依赖是指两个或多个Bean相互依赖,形成一个闭环。例如,Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean A。又或者 Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean C,Bean A 依赖于 Bean A。

        循环依赖一般由构造器注入或Setter注入造成。

2.举例

例:由构造器注入的循环依赖

2.1Java代码

public class AccountService {
 private RoleService roleService;
 public AccountService(RoleService roleService) {
 this.roleService = roleService;
 }
 public void test() {
 System.out.println("AccountService Test...");
 }
}

public class RoleService {
 private AccountService accountService;
 public RoleService(AccountService accountService) {
 this.accountService = accountService;
 }
 public void test() {
 System.out.println("RoleService Test...");
 }
}

2.2XML配置

<!--
 如果多个bean对象中互相注⼊,则会出现循环依赖的问题
 可以通过set⽅法注⼊解决
-->
<bean id="accountService" class="com.xxxx.service.AccountService">
 <constructor-arg name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.xxxx.service.RoleService">
 <constructor-arg name="accountService" ref="accountService"/>
</bean>

二、解决方案

1.将构造器注⼊改为set⽅法注⼊

public class AccountService {
 private RoleService roleService;
 /* public AccountService(RoleService roleService) {
 this.roleService = roleService;
 }*/
 public void setRoleService(RoleService roleService) {
 this.roleService = roleService;
 }
 public void test() {
 System.out.println("AccountService Test...");
 }
}
public class RoleService {
 private AccountService accountService;
 /* public RoleService(AccountService accountService) {
 this.accountService = accountService;
 }*/
 public void setAccountService(AccountService accountService) {
 this.accountService = accountService;
 }
 public void test() {
 System.out.println("RoleService Test...");
 }
}
<!--
 <bean id="accountService" class="com.xxxx.service.AccountService">
 <constructor-arg name="roleService" ref="roleService"/>
 </bean>
 <bean id="roleService" class="com.xxxx.service.RoleService">
 <constructor-arg name="accountService" ref="accountService"/>
 </bean>
-->
<!--修改为set⽅法注⼊-->
<bean id="accountService" class="com.xxxx.service.AccountService">
 <property name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.xxxx.service.RoleService">
 <property name="accountService" ref="accountService"/>
</bean>

2.三级缓存

2.1三级缓存机制

一级缓存(singletonObjects):用于存储完全初始化好的单例 Bean。类型:Map<String, Object>

二级缓存(earlySingletonObjects):用于存储早期暴露的 Bean 实例,部分初始化的 Bean。Map<String, Object>

三级缓存(singletonFactories):用于存储 Bean 工厂,主要用于创建 Bean 的代理对象。Map<String, ObjectFactory<?>>

2.2解决流程

①在创建 A 对象放入到 spring 容器的过程,先看一级缓存,能否可以直接获取到 A,如果可以,直接获取,如果不可以,则开始创建 A 对象,A创建过程中发现需要属性 B,查找发现 B 还没有在一级缓存中,于是先将 A 放到三级缓存中,此时的 A 不完整,没有属性,但是可以引用。接下来就去实例化B。

②B 实例化的过程,也是先从一级缓存,看自己有没有,没有的话,开始创建,此时发现需要A,于是B先查一级缓存寻找A,如果没有,再查二级缓存,如果还没有,再查三级缓存,找到了A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。

③B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中的状态)。然后回来接着创建A,此时B已经创建结束,可以直接从一级缓存里面拿到B,去完成A的创建,并将A放到一级缓存。

简单来说

在Spring的Bean创建过程中,如果发生循环依赖,Spring会先检查一级缓存中是否已存在所需的Bean,如果不存在,则检查二级缓存,如果二级缓存中也不存在,则检查三级缓存中的ObjectFactory,通过ObjectFactory来创建Bean,并将其放入二级缓存中,待属性填充完成后,再将其放入一级缓存中。

3.懒加载

lazy-init属性(懒加载)

如果为false,则在IOC容器启动时会实例化bean对象,默认false。

如果为true,则IOC容器启动时不会实例化Bean对象,在使⽤bean对象时才会实例化。

注意:请在具体的情境中选用适合的方法来解决循环依赖问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值