循环依赖:就是 N 个类循环(嵌套)使用。
简单来说,就是多个 Bean 之间互相依赖或引用到对方,最终形成了 闭环。循环依赖的关系可以表示成下图
上图中的 A,B,C 代表三个不同的对象,上图表示了三者的引用关系。
循环依赖不仅发生在多个类中,还可能发生在本类中,即 N = 1,这种事极限情况的循环依赖:自己依赖自己
注意:这里指的循环依赖不是方法之间的循环调用,而是对象的相互依赖关系。(因为方法之间的循环调用有出口也能正常运行)
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(new A());
}
}
class A {
public A() {
new B();
}
}
class B {
public B() {
new A();
}
}
运行结果
Exception in thread "main" java.lang.StackOverflowError
从上面代码可以看到,若构造函数之间发生循环依赖(A的构造方法中依赖B,B的构造方法中依赖A),程序会在运行时一直循环调用最终导致内存溢出。
1.构造器注入循环依赖
@Service
public class A {
public A(B b) {
}
}
@Service
public class B {
public B(A a) {
}
}
运行结果
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
构造器注入构成的循环依赖是无法解决的!只能抛出
BeanCurrentlyInCreationException异常表示循环依赖。
错误原因:
根本原因是 Spring 解决循环依赖依靠的是 Bean 的 中间态 这个概念,而中间态指的是 已经实例化,但未初始化的状态。而构造器负责的是实例化,故无法解决构造器循环依赖。
2.field属性注入(setter方法注入)循环依赖
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
最常用的依赖注入方式,不会出现循环依赖问题(省略 setter 方式的注入实现代码,类似)
3.prototype field 属性注入循环依赖
prototype 在平时使用情况较少,仍需重视
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class A {
@Autowired
private B b;
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class B {
@Autowired
private A a;
}
此处代码在运行时不会报错,因为非单例Bean默认不会初始化,而是使用时才会初始化。
手动 getBean() 或者在一个单例 Bean 内 @Autowired A 或 B 就会出错
// 在单例Bean内注入
@Autowired
private A a;
注入运行结果
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mytest.TestSpringBean': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
错误解决方案
使用 @Lazy:@Lazy 只是延迟初始化,当真正需要使用到开始初始化时,依旧会出现上面的异常。
1.不能解决的情况:
2.能够解决的情况:
由初始化 单例Bean 的流程图可以看出,循环依赖主要发生在第二步(populateBean),也就是 field属性注入的处理
Spring 容器的整个生命周期中,单例Bean对象是唯一的。即可以使用缓存来加速访问
Spring 源码中使用了大量的 Cache 手段,其中在循环依赖问题的解决过程中就使用了“三级缓存”
三级缓存的意义
除了三级缓存,还有另外两个缓存
代码示例
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
// 从上至下 分表代表这“三级缓存”
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
...
/** Names of beans that are currently in creation. */
// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~
// 它在Bean开始创建时放值,创建完成时会将其移出~
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Names of beans that have already been created at least once. */
// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复
// 至少被创建了一次的 都会放进这里~~~~
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.先从一级缓存中获取,获取到直接返回
Object singletonObject = this.singletonObjects.get(beanName);
//2.如果获取不到或对象正在创建,就到二级缓存中去获取,获取到直接返回
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
//3.如果仍获取不到,且允许 singletonFactories(allowEarlyCurrentlyInCreation())通过 getObject()获取。
//就到三级缓存中用 getObject() 获取。
//获取到就从 singletonFactories中移出,且放进 earlySingletonObjects。
//(即从三级缓存移动到二级缓存)
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
...
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
protected boolean isActuallyInCreation(String beanName) {
return isSingletonCurrentlyInCreation(beanName);
}
...
}
加入 singletonFactories 三级缓存的前提是执行了构造器,所以构造器的循环依赖无法解决。
getSingleton() 从缓存里获取单例对象步骤分析可知,Spring 解决循环依赖的重点:在于 singletonFactories这个三级缓存。此 Cache 存放着 ObjectFactory,是解决问题的关键。
// 它可以将创建对象的步骤封装到ObjectFactory中 交给自定义的Scope来选择是否需要创建对象来灵活的实现scope。 具体参见Scope接口
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
经过 ObjectFactory.geteObject() 后,此时放进了 二级缓存 earlySingletonObjects 内的这个对象就已经实例化好了,虽然不够完善,但是该对象的引用 已经可以被其他引用了。
补充:
二级缓存 earlySingletonObjects 中的数据何时进行 添加、删除?