这一次完全搞懂Spring中的循环引用
我们有一个A,引用B:
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
public B getB() {
return b;
}
@Override
public String toString() {
return "{A: " + hashCode() + " B: " + b.hashCode() + "}";
}
}
我们有一个B,引用A:
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
public A getA() {
return a;
}
@Override
public String toString() {
return "{B: " + hashCode() + " A: " + a.hashCode() + "}";
}
}
此时它们之间是循环引用的。我们好奇的是Spring如何处理这种循环引用,这需要分几种情况讨论。
AB都是Singleton,无AOP,不是AUTOWIRE_CONSTRUCTOR
将A和B的bean definition都注册到factory中,并且将它们的autowire mode设置一下,这样就会产生彼此注入的情况:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
RootBeanDefinition aRbd = new RootBeanDefinition();
aRbd.setBeanClass(A.class);
aRbd.setScope(BeanDefinition.SCOPE_SINGLETON);
aRbd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
RootBeanDefinition bRbd = new RootBeanDefinition();
bRbd.setBeanClass(B.class);
bRbd.setScope(BeanDefinition.SCOPE_SINGLETON);
bRbd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
factory.registerBeanDefinition("a", aRbd);
factory.registerBeanDefinition("b", bRbd);
A a = (A) factory.getBean("a");
B b = (B) factory.getBean("b");
System.out.println(a);
System.out.println(b);
getBean("a")
,创建A的实例- 填充A的属性,发现它依赖B,此时
getBean("b")
,创建B的实例 - 填充B的属性,发现B依赖A,循环引用发生
此时,考虑你是BeanFactory,你可以这样做:
- 对于singleton对象,创建后就将它放到SingletonRegistry中
- 此时B填充属性
a
时,getBean("a")
会在SingletonRegistry中发现a已经创建的实例,不会再次创建a - 此时B中的属性
a
从SingletonRegistry的缓存中得到满足,从而创建成功,从而A可以将它设置到A.b
上 - 循环依赖得到满足
注意:Spring并不是这样做的哦,Spring只有在对象完全创建并填充完毕后才会将它放到SingletonRegistry(的一级缓存)中,这里只是说这样做在AB都是Singleton、无AOP并且自动装配模式不是通过构造器的情况下行得通。我们后面的讲述会基于Spring已经将它放到SingletonRegistry的前提进行,然后发现只是这样的话在某些条件下行不通,然后我们再推翻这个理论。
结果:
{A: 1659791576 B: 1935365522}
{B: 1935365522 A: 1659791576}
AB都是Singleton,无AOP,AUTOWIRE_CONSTRUCTOR
但如果对A和B的自动装配模式都是通过构造器呢?这样的话,你没有机会先创建出一个A,因为在构造器上你就要创建出B的实例并设置进去,并且在B的构造器中,你也要创建出A的实例并注入进去,死锁了。
给A和B分别提供构造方法,并只提供一个构造方法,这里我就不贴代码了,然后将AutowireMode改成AUTOWIRE_CONSTRUCTOR
:
public static void main(String[] args) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
RootBeanDefinition aRbd = new RootBeanDefinition();
aRbd.setBeanClass(A.class);
aRbd.setScope(BeanDefinition.SCOPE_SINGLETON);
aRbd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
RootBeanDefinition bRbd = new RootBeanDefinition();
bRbd.setBeanClass(B.class);
bRbd.setScope(BeanDefinition.SCOPE_SINGLETON);
bRbd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
factory.registerBeanDefinition("a", aRbd);
factory.registerBeanDefinition("b", bRbd);
A a = (A) factory.getBean("a");
B b = (B) factory.getBean("b");
System.out.println(a);
System.out.println(b);
}
明显失败:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through constructor parameter 0;
实际上,这种构造方法注入是在AbstractBeanFactory
的createBeanInstance
中实现的,在多种情况下都会尝试使用构造函数注入:
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
AB都是Singleton,并且有AOP
考虑这时A上有一个AOP通知,Spring必须为A创建一个代理类,并将代理类放到factory中,但AOP代理的创建在初始化阶段的BeanPostProcessor应用时,而它却在属性填充阶段就需要设置给B了:
- A创建,属性填充,
getBean("b")
- B创建,属性填充,
getBean("a")
,通过singleton获取到a的实例并设置给B.a
- 设置创建出的b给
A.b
,完成属性填充阶段,进入初始化阶段 - 执行BeanPostProcessor,返回AOP代理
第四步返回的代理实际是原始A的一个包装,而B目前引用的还是原始A,它调用A将得不到任何AOP增强。所以,上面说的在Singleton创建后就将它注册到SingletonRegistry的逻辑行不通。
Spring是如何解决的呢?
SingletonRegistry
中有三种缓存,它们都是beanName到对应缓存对象的Map:
singletonObject
:一级缓存,存放完整的,创建完毕且初始化完好的单例beanearlySingletonObjects
:二级缓存,存放由三级缓存的factory创建出来的对象singletonFactories
:三级缓存,存放获取早期singleton对象的factory方法
Spring在创建singleton时,会直接将它加入到三级缓存中:
// 如果bean是singleton、并且允许循环引用、并且当前bean在创建过程中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 通过调用SingletonRegistry的`addSingletonFactory`一个lambda函数添加到其内部的三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
而在其内部的getSingleton
中,会先尝试获取一级缓存、失败后获取二级缓存、再失败通过三级缓存中缓存的工厂方法来获取singleton,并将其加入到二级缓存
@Nullable
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);
if (singletonFactory != null) {
// 调用三级缓存的函数获取返回的singleton对象,然后加到二级缓存中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
这样的话,B注入A时再通过getBean("a")
获取,就会由于三级缓存导致注册进去的lambda表达式被执行的效果,实际上也就是getEarlyBeanReference
被执行,然后,它返回的对象将被持久化到二级缓存中。
我们看下getEarlyBeanReference
是啥,它允许所有的SmartInstantiationAwareBeanPostProcessor
的getEarlyBeanReference
执行,并返回一个对象:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
实际上,这个SmartInstantiationAwareBeanPostProcessor
正是给AOP用于处理循环引用的,它允许AOP代理对象提前创建,并将原始bean包裹。这样,一旦出现循环依赖,所有AOP代理对象都会提前创建好,然后保存到二级缓存中。这样可以避免每次循环引用getEarlyBeanReference
时都返回新的代理对象,这样又达不到效果了。
至此为止,Singleton对象的循环引用已经差不多了,Spring通过三级缓存以及提前创建AOP代理对象来保证循环引用的情况下也能正常创建对象。