Loading

这一次完全搞懂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);
  1. getBean("a"),创建A的实例
  2. 填充A的属性,发现它依赖B,此时getBean("b"),创建B的实例
  3. 填充B的属性,发现B依赖A,循环引用发生

此时,考虑你是BeanFactory,你可以这样做:

  1. 对于singleton对象,创建后就将它放到SingletonRegistry中
  2. 此时B填充属性a时,getBean("a")会在SingletonRegistry中发现a已经创建的实例,不会再次创建a
  3. 此时B中的属性a从SingletonRegistry的缓存中得到满足,从而创建成功,从而A可以将它设置到A.b
  4. 循环依赖得到满足

注意: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;

实际上,这种构造方法注入是在AbstractBeanFactorycreateBeanInstance中实现的,在多种情况下都会尝试使用构造函数注入:

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了:

  1. A创建,属性填充,getBean("b")
  2. B创建,属性填充,getBean("a"),通过singleton获取到a的实例并设置给B.a
  3. 设置创建出的b给A.b,完成属性填充阶段,进入初始化阶段
  4. 执行BeanPostProcessor,返回AOP代理

第四步返回的代理实际是原始A的一个包装,而B目前引用的还是原始A,它调用A将得不到任何AOP增强。所以,上面说的在Singleton创建后就将它注册到SingletonRegistry的逻辑行不通

Spring是如何解决的呢?

SingletonRegistry中有三种缓存,它们都是beanName到对应缓存对象的Map:

  1. singletonObject:一级缓存,存放完整的,创建完毕且初始化完好的单例bean
  2. earlySingletonObjects:二级缓存,存放由三级缓存的factory创建出来的对象
  3. 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是啥,它允许所有的SmartInstantiationAwareBeanPostProcessorgetEarlyBeanReference执行,并返回一个对象:

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代理对象来保证循环引用的情况下也能正常创建对象。

Prototype对象的循环引用

posted @ 2023-12-04 21:53  yudoge  阅读(171)  评论(0编辑  收藏  举报