循环依赖就是两个或两个以上的对象相互依赖,形成了环状的依赖关系。
class A{
public B b;
}
class B{
public A a;
}
A a = new A();
B b = new B();
a.b = b;
b.a = a;
上述代码描述了两个对象之间的相互依赖关系。
由于在 Spring 中,Bean 的生命周期是由 Spring 进行管理,所以这种依赖关系在 Bean 的创建过程中,就会出现循环依赖的问题。在 Spring 中出现循环依赖的场景由多种,其中 Spring 会解决部分循环依赖问题,而 Spring 无法解决的,就需要在开发中避免或自己去解决。
Bean 的生命周期可以简化为以下几点:
BeanDefinition
BeanDefinition
,通过反射调用构造方法,生成 bean 的原始对象(可能会发生构造器循环依赖)。singletonObjects
构造器循环依赖 :两个或多个 Bean 通过构造函数相互依赖时,就会发生构造器循环依赖。例如,BeanA 的构造函数参数需要 BeanB,而 BeanB 的构造函数参数需要 BeanA。这种循环依赖是无法解决的,因为在创建 Bean 实例时需要提供所有的构造函数参数,而循环依赖导致无法满足这个条件。
代码实例如下:
@Component
public class AService {
public BService bService;
public AService(BService bService) {
this.bService = bService;
}
public void test() {
System.out.println("bService = " + bService);
}
}
@Component
public class BService {
public AService aService;
public BService(AService aService) {
this.aService = aService;
}
public void test() {
System.out.println("aService = " + aService);
}
}
上述代码是存在构造器循环依赖的。
产生原因:
getBean()
方法,在容器内寻找BService;doCreateBean()
方法,去实例化 BService;解决方法:
在 AService 中的构造方法上,添加 @Lazy
注解。
当 AService 中的构造方法,添加该注解后,在调用 AService 的构造方法时,Spring 后创建一个 BService 的代理对象,给 AService 的构造方法去使用,以此来实例化 AService,并完成后续的生命周期。
@Component
public class AService {
public BService bService;
public void test() {
System.out.println("bService = " + bService);
}
}
@Component
public class BService {
public AService aService;
public void test() {
System.out.println("aService = " + aService);
}
}
上述代码存在属性方法间的循环依赖,该类循环依赖,由 Spring 通过三级缓存方式,进行了解决。
产生原因(假设不存在三级缓存):
解决思路:
添加一层缓存进行解决:
在对象进行实例化后,将实例化好的对象,提前暴露,放到一个缓存中,后续有对象需要该对象,去缓存中寻找。
如下图所示:
通过上述流程可以看出,二级缓存就能够解决 Spring 的属性循环依赖,为什么需要三级缓存呢?
假设A的原始对象为@00a1,A并且需要进行AOP,得到的代理对象为@00a2。
由于B对象中的A对象,得到的是A的原始对象,并非代理对象。
此时就会出现不一致问题,即 B.A = @00a1,通过容器获取的A对象为@00a2。
因此需要三级缓存。
一级缓存:singletonObjects,也就是平时所说的单例池,存放的经过完整的生命周期的 bean 对象。
二级缓存:earlySingletonObjects,存放的是早期的 bean 对象,是没有经过完整生命周期的 bean 对象。
三级缓存:singletonFactories,对象工厂,用于创建早期 bean 对象的工厂。
// 1.单例bean;2.允许循环依赖;3.正在创建的bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 循环依赖-添加到三级缓存,此处缓存的val是lamda表达式,当需要从三级缓存中获取bean时,才会执行该表达式
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
getSingleton()
方法解析 /*
该方法主要流程:
1.首先查询一级缓存,若存在,则返回,否则去查询二级缓存
2.查询二级缓存,若存在,则返回,否则通过三级缓存去创建
3.创建完毕,将不完整的bean,放入二级缓存,删除三级缓存中相应信息,并返回
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先去一级缓存查询,判断是否可以查询到
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 若查询不到,则去二级查询去找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 加锁,重复上述流程,保证原子性
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 若一二级缓存中都没有该bean,则通过三级缓存创建bean
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 放入二级缓存,此bean没有经过完整的生命周期,不能放入一级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中移除该bean的相关信息
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
此时便通过三级缓存机制,解决了 Spring 的属性循环依赖问题。