一、循环依赖的概念
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对象时才会实例化。
注意:请在具体的情境中选用适合的方法来解决循环依赖问题!