spice and wolfspice and wolf Be the One you wanna Be

spring依赖注入的循环依赖问题

为什么会出现循环依赖问题?

(前提知识点:Bean的生命周期会有四个阶段,包括实例化、属性赋值、初始化和销毁)而在spring容器主函数调用的时候会扫描class文件生成BeanDefinition对象,然后创建单例bean存储到单例池中,这时会调用createBean()方法,而createBean()中真正创建实例的是doCreateBean()方法,doCreateBean()方法包括了Bean生命周期中的实例化、属性赋值、初始化逻辑。在执行到属性赋值populateBean()

时会进行依赖注入操作,如果属性有@Autowired注解修饰,则会调用getBean()方法在单例池中查找对应的Bean实例,如果单例池中没有对应的Bean实例则会执行createBean()创建属性类的Bean实例。这时下列场景就会出现问题。

服务A注入服务B:调用createBean()创建A实例,创建A实例时调用A的populateBean()会getBean(),然后调用createBean()创建B实例。

服务B注入服务A:调用createBean()创建B实例,创建A实例时调用A的populateBean()会getBean(),然后调用createBean()创建A实例。

这时就会出现循环调用,这就是循环依赖问题。

如何判断是否出现了循环依赖?

spring会维护一个singletonsCurrentlyInCreation的Set结构,这个Set用来存储单例的beanName。通过判断在singletonsCurrentlyInCreation中是否存在当前beanName的元素即可判断是否触发了循环依赖。

这个Set结构有三个关键函数:

    // DefaultSingletonBeanRegistry.class
    
    public boolean isSingletonCurrentlyInCreation(String beanName) { // [0.1]
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

    protected void beforeSingletonCreation(String beanName) { // [0.2]
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

    protected void afterSingletonCreation(String beanName) { // [0.3]
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }
  • isSingletonCurrentlyInCreation()用来判断当前bean的单例是否在创建中,等同于判断循环依赖,所以也是用来判断是否触发循环依赖的关键方法。
  • beforeSingletonCreation()在创建单例bean之前调用,作用是将当前bean的beanName加入到Set(判断循环依赖的依据)中。
  • afterSingletonCreation()在创建单例bean之后调用,将beanName从Set中删除。

在出现循环依赖时代码的执行流程

在阐述流程前我们先提一下循环依赖中的三级缓存机制:

  • singletonObjects(一级缓存)。单例池,用来存储走完完整Bean创建流程的Bean普通对象或者Bean代理对象。
  • earlySingletonObjects(二级缓存)。用来存储经过AOP后的动态反射对象。
  • singleFactories(三级缓存)。用来存储基于bean对象的拉姆达表达式。

首先spring容器(applicationContext)在系统启动后会做这么两件事情:

  1. 扫描项目输出目录中的class文件,包装成DefinitionBean数据结构放在一个map中。
  2. 遍历所有作用域为单例的bean对象,创建单例池。

而在单例创建过程中我们假设会先创建AService单例。

创建AService单例

创建AService单例时会调用getBean(beanName)方法获取单例bean,getBean()多个重载方法都是通过调用doGetBean()方法实现。

    // AbstractBeanFactory.class
    
    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        Object sharedInstance = this.getSingleton(beanName); // [1.1]
        Object bean;
        if (sharedInstance != null && args == null) { [1.2]
            ...
            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
        } else {
            ...

            try {
                RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
                ...

                // 判断是否是单例
                if (mbd.isSingleton()) {
                    // 获取单例
                    sharedInstance = this.getSingleton( // [1.3]
                        beanName, 
                        () -> {
                            try {
                                return this.createBean(beanName, mbd, args);
                            } catch (BeansException var5) {
                                this.destroySingleton(beanName);
                                throw var5;
                            }
                        }
                    );
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                } else if (mbd.isPrototype()) {
                    ...
                } else {
                    ...
                }
            } catch (BeansException var26) {
                this.cleanupAfterBeanCreationFailure(beanName);
                throw var26;
            }
        }

        if (requiredType != null && !requiredType.isInstance(bean)) {
            ...
        } else {
            return bean;
        }
    }

先看看this.getSingleton(beanName) [1.1]的返回结果:

    // DefaultSingletonBeanRegistry.class

    @Nullable
    public Object getSingleton(String beanName) {  // [1.1]处调用的方法
        return this.getSingleton(beanName, true);
    }

    // 当允许循环依赖(allowEarlyReeference)时,会有三种可能性
    // 1. 当一级缓存二级缓存中都没有对应bean,且没有触发循环依赖(即this.isSingletonCurrentlyInCreation),则返回null
    // 2. 当一级缓存二级缓存中都没有对应bean,但是触发了循环依赖时,且三级缓存不为空时,则基于三级缓存的拉姆达表达式生成对应代理对象并放到二级缓存中,并删除三级缓存中对应的项(这里使用了双重检查锁)。
    // 3. 当一级缓存中存在对应的bean,则返回一级缓存
    // 4. 当二级缓存中存在对应的bean,则返回二级缓存
    protected Object getSingleton(String beanName, boolean allowEarlyReference) { // [2]
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { // [2.1]
            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 = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }

[2.1]因为现在在单例池即一级缓存中还拿不到对应的bean单例且isSingletonCurrentlyInCreation(beanName)[0.1]返回false(singletonsCurrentlyInCreation中没有对应beanName的数据),所以this.getSingleton(beanName) [1.1]返回null,即sharedInstance为null,所以[1.2]处的if判断会走else分支。

然后会拿到beanDefinition,进入单例的创建过程this.getSingleton(beanName, singletonFactory) [1.3]:

    // DefaultSingletonBeanRegistry.class
    
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized(this.singletonObjects) { // [3.1]
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) { [3.2]
                ...

                // 将当前beanName加入到singletonsCurrentlyInCreation的Set中,用来判断是否出现循环依赖
                this.beforeSingletonCreation(beanName); // [3.3]
                boolean newSingleton = false;
                ...

                try {
                    singletonObject = singletonFactory.getObject(); // [3.4]
                    newSingleton = true;
                    ...
                } catch (IllegalStateException var16) {
                    ...
                } catch (BeanCreationException var17) {
                    ...
                } finally {
                    ...

                    // 将当前beanName从singletonsCurrentlyInCreation删除
                    this.afterSingletonCreation(beanName);
                }

                if (newSingleton) {
                    this.addSingleton(beanName, singletonObject);
                }
            }

            return singletonObject;
        }
    }

从上往下执行

[3.1]首先会进入以this.singletonObjects为锁的同步代码块,这里需要注意下this是beanFactory即bean工厂,同一个bean工厂下的bean实例创建都是以工厂的单例池作为锁以此保证并发安全

[3.2]因为单例池中没有对应bean实例,所以进入if语句块中

[3.3]进入[0.2]逻辑,将当前beanName加入到singletonsCurrentlyInCreation中,表示当前bean中在创建中

[3.4]拿到singletonFactory的执行结果

从上文代码看出[1.3]的第二个入参singletonFactory实则是一个拉姆达表达式,表达式执行结果是createBean()的返回值:

() -> {
    try {
        return this.createBean(beanName, mbd, args);
    } catch (BeansException var5) {
        this.destroySingleton(beanName);
        throw var5;
    }
}

这里我们需要深入探究createBean(beanName, mad, args) 方法的执行逻辑,而createBean方法的核心是doCreateBean(beanName, mbdToUse, args)方法,该方法中包含了我们熟知的Bean生命周期逻辑即实例化、属性赋值和初始化:

    // AbstractAutowireCapableBeanFactory.class

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }

        // 实例化
        if (instanceWrapper == null) {
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }

        Object bean = instanceWrapper.getWrappedInstance();
        ...

        // 判断是否是正在创建的单例Bean
        boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName); // [4.1]
        if (earlySingletonExposure) { // [4.2]
            ...

            // 添加到三级缓存 [4.3]
            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }

        Object exposedObject = bean;

        try {
            // 属性赋值 [4.4]
            this.populateBean(beanName, mbd, instanceWrapper);
            // 初始化
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
            if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
                throw (BeanCreationException)var18;
            }

            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
        }

        if (earlySingletonExposure) { // [4.5]
            Object earlySingletonReference = this.getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
                    ...
                }
            }
        }

        try {
            this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
            return exposedObject; // [4.6]
        } catch (BeanDefinitionValidationException var16) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
        }
    }

因为我们这里只探究循环依赖是如何被解决的,所以与该问题没有太大关联的代码逻辑我们直接一笔带过。

[4.1]中mbd.isSingleton()为true(表示当前bean为单例,一般bean作用域默认都是singleton),我们默认this.allowCircularReferences为true,[3.3]中在singletonsCurrentlyInCreation添加了当前bean的beanName所以[0.1]的结果也是true,所以earlySingletonExposure值为true,会进入[4.2]if语句块。

[4.3]将一个lambda表达式加入到三级缓存中,代码如下:

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized(this.singletonObjects) { // [5.1]
            if (!this.singletonObjects.containsKey(beanName)) { // [5.2]
                this.singletonFactories.put(beanName, singletonFactory); // 加入三级缓存
                this.earlySingletonObjects.remove(beanName); // 删除二级缓存中的对应bean,二级三级缓存中不能存在同一个bean实例
                this.registeredSingletons.add(beanName);
            }

        }
    }

[5.1]同样是把this.singletonObjects作为同步锁,因为synchronized是可重入锁,所以正常执行代码块,并不会锁住

[5.2]单例池没有对应 beanName的实例,所以会执行if语句代码块:lambda表达式加入三级缓存,并且删除二级缓存中的对应bean对象,在this.registeredSingletons加入beanName(这个影响不用太关注)

前面都是铺垫,循环依赖问题发生时机就是在属性赋值实现依赖注入时,所以我们需要看this.populateBean(beanName, mbd, instanceWrapper)[4.4]的代码逻辑:

    // AbstractAutowireCapableBeanFactory.class

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        if (bw == null) {
            ...
        } else {
            ...
            int resolvedAutowireMode = mbd.getResolvedAutowireMode();
            if (resolvedAutowireMode == 1 || resolvedAutowireMode == 2) {
                ...
                if (resolvedAutowireMode == 1) {
                    this.autowireByName(beanName, mbd, bw, newPvs); // [6.1]
                }

                if (resolvedAutowireMode == 2) {
                    this.autowireByType(beanName, mbd, bw, newPvs);
                }

                ...
            }

            ...
        }
    }

关注依赖注入方法this.autowireByName(beanName, mbd, bw, newPvs)[6.1]:

    // AbstractAutowireCapableBeanFactory.class

    protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        String[] propertyNames = this.unsatisfiedNonSimpleProperties(mbd, bw);
        String[] var6 = propertyNames;
        int var7 = propertyNames.length;

        for(int var8 = 0; var8 < var7; ++var8) {
            String propertyName = var6[var8];
            if (this.containsBean(propertyName)) {
                // 获取bean实例
                Object bean = this.getBean(propertyName); // [7.1]
                pvs.add(propertyName, bean);
                this.registerDependentBean(propertyName, beanName);
                ...
            } else if (this.logger.isTraceEnabled()) {
                ...
            }
        }
    }

这里会遍历bean中被@Autowired修饰的属性,如果在BeanDefinitionMap中有对应的bean数据,则会调用getBean(propertyName)[7.1]方法获取bean实例,这时细心的同学就会发现这不就是创建AService单例时最初调用的函数吗?getBean在获取BService的单例的时候会执行跟AService同样的代码逻辑,最后也会进入[6.1]进行依赖注入,这时getBean会再次尝试获取AService的实例,看来我们来到了进入下一个循环的起点——第二次尝试通过getBean获取AService单例(其实第一次getBean获取AService单例的创建过程并没有走完,但作为循环依赖的一个关键节点,我们会在后面的小节说明AService单例创建的后续操作)。

重走西游,用AService的beanName再次调用getBean(beanName)方法

如上文所述,getBean()底层调用了doGetBean(),让我们看看这次的执行逻辑:

    // AbstractBeanFactory.class
    
    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        Object sharedInstance = this.getSingleton(beanName); // [1.1]
        Object bean;
        if (sharedInstance != null && args == null) { [1.2]
            ...
            bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null); // [1.4]
        } else {
            ...

            try {
                RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
                ...

                // 判断是否是单例
                if (mbd.isSingleton()) {
                    // 获取单例
                    sharedInstance = this.getSingleton( // [1.3]
                        beanName, 
                        () -> {
                            try {
                                return this.createBean(beanName, mbd, args);
                            } catch (BeansException var5) {
                                this.destroySingleton(beanName);
                                throw var5;
                            }
                        }
                    );
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                } else if (mbd.isPrototype()) {
                    ...
                } else {
                    ...
                }
            } catch (BeansException var26) {
                this.cleanupAfterBeanCreationFailure(beanName);
                throw var26;
            }
        }

        if (requiredType != null && !requiredType.isInstance(bean)) {
            ...
        } else {
            return bean;
        }
    }

先看[1.1]的返回结果:

    // DefaultSingletonBeanRegistry.class

    @Nullable
    public Object getSingleton(String beanName) {  // [1.1]处调用的方法
        return this.getSingleton(beanName, true);
    }

    // 当允许循环依赖(allowEarlyReeference)时,会有三种可能性
    // 1. 当一级缓存二级缓存中都没有对应bean,且没有触发循环依赖(即this.isSingletonCurrentlyInCreation),则返回null
    // 2. 当一级缓存二级缓存中都没有对应bean,但是触发了循环依赖时,且三级缓存不为空时,则基于三级缓存的拉姆达表达式生成对应代理对象并放到二级缓存中,并删除三级缓存中对应的项(这里使用了双重检查锁)。
    // 3. 当一级缓存中存在对应的bean,则返回一级缓存
    // 4. 当二级缓存中存在对应的bean,则返回二级缓存
    protected Object getSingleton(String beanName, boolean allowEarlyReference) { // [2]
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { // [2.1]
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) { // [2.2]
                synchronized(this.singletonObjects) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) { // [2.3]
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) { // [2.4]
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) { // [2.5]
                                singletonObject = singletonFactory.getObject(); // [2.6] 
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }

因为[2.1][2.2][2.3][2.4][2.5]判断都为true,所以会执行拉姆达三级缓存中存储的拉姆达表达式[2.6],先看看拉姆达表达式长啥样,即[4.3]中的第二个参数:

() -> {
    return this.getEarlyBeanReference(beanName, mbd, bean);
}

getEarlyBeanReference执行如下:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { // [8.2]
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) { // [8.1]
            Iterator var5 = this.getBeanPostProcessors().iterator();

            while(var5.hasNext()) {
                BeanPostProcessor bp = (BeanPostProcessor)var5.next();
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }

        return exposedObject;
    }

在if[8.1]语句成立时貌似做了一些事情,这里我们后面再说,现在我们只需要知道此处[1.1]返回的sharedInstance不为null,且args为null,所以[1.2]的条件成立,执行[1.4]返回bean实例(实际上是提前暴露的Bean代理对象)

我们再回到[2.6]获取到AService的Bean代理对象,并存储到二级缓存中,将三级缓存中对应beanName的键值对删除。

可以看到触发循环依赖后就不会走createBean()流程而是直接返回AService的AOP代理对象,到这里我们发现其实循环依赖已经被解决了,但是这时候注入到BService的AService对象其实是没有完全走完bean生命周期的代理对象,从[8.2]其实可以看出,本应在初始化阶段做的AOP代理提前到属性赋值阶段完成了,顾名思义提前引用所以叫earlyBeanReference。

此时我们需要想到一个问题,在AService生命周期的初始化阶段如果再次创建一个AService的代理对象,会不会破坏AService的单例性质?答案是会,所以我们需要一个地方存储各个bean的唯一代理对象或者原始对象(没有AOP的情况),来保证进行AOP代理时不会创建出多个AOP代理从而破坏bean的单例性质。

九九归一,完整单例创建的后续操作

本例中BService其实只是作为复现循环依赖场景的一个工具人,所有过程和AService单例创建相同,所以我们跳过它的执行过程,直接快进到最外层(即AService)[3.4]这行代码,:

    // DefaultSingletonBeanRegistry.class
    
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized(this.singletonObjects) { // [3.1]
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) { [3.2]
                ...

                // 将当前beanName加入到singletonsCurrentlyInCreation的Set中,用来判断是否出现循环依赖
                this.beforeSingletonCreation(beanName); // [3.3]
                boolean newSingleton = false;
                ...

                try {
                    singletonObject = singletonFactory.getObject(); // [3.4]
                    newSingleton = true;
                    ...
                } catch (IllegalStateException var16) {
                    ...
                } catch (BeanCreationException var17) {
                    ...
                } finally {
                    ...

                    // 将当前beanName从singletonsCurrentlyInCreation删除
                    this.afterSingletonCreation(beanName);
                }

                if (newSingleton) {
                    this.addSingleton(beanName, singletonObject);
                }
            }

            return singletonObject;
        }
    }

[3.4]实际返回值就是doCreateBean()方法的返回值[4.6]。
因为AService实例创建基本结束,这里调用this.afterSingletonCreation(beanName)[0.3]将beanName从singletonCurrentlyInCreation中删除。
最后看看this.addSingleton(beanName, singletonObject)干了什么:

    // DefaultSingletonBeanRegistry.class

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized(this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            ...
        }
    }

没错,如预料一样无趣,就是将[4.6]的返回值作为bean的实例放到一级缓存(单例池)中,然后将其从二级缓存和三级缓存中删除。至此循环依赖场景下Spring的执行完整过程就讲解完毕。后面我们浅析一下各个数据结构的作用。

spring如何解决循环依赖的?

熟知的循环依赖如何形成的(假设没有二级缓存和三级缓存及相关逻辑)?

  1. 创建A的bean实例(getBean()调用doGetBean(),doGetBean()里面会调用createBean()方法),createBean()会执行实例化、属性赋值、初始化,在属性赋值时会进行依赖注入,此时会创建B的bean实例。
  2. B单例创建时依然会执行B的createBean()方法,然后在b的属性赋值时,会调用getBean(beanName)方法获取A实例。
  3. 再次通过getBean(beanName)方法获取A实例,这时如果只有一级缓存,因为最外层A的实例创建过程没有执行完,所以从一级缓存中并找不到bean对象,会再次执行A实例的创建操作,从而形成死循环。

只有一级缓存够不够?

只有一级缓存,在创建的过程中会在Bean未完全初始化就被引用,导致属性未注入或方法不可用。因为只有一级缓存,所以外部进程使用bean的方法时也会直接从该缓存中取,而为了防止循环依赖,需要实例化后属性赋值前将bean实例放入一级缓存中,所以在这时有外部进程使用该实例时,会出现获取的属性值为null的情况,所以一级缓存并不能解决循环依赖问题。

两级缓存能解决循环依赖吗(没有第二级缓存及相关逻辑)?

  • 若提前将‌代理对象‌放入二级缓存:违背 Spring“初始化完成后才创建代理”的设计原则,且若无循环依赖则白白生成代理,浪费资源
  • 若提前将‌原始对象‌放入二级缓存:当该 Bean 实际需要 AOP 代理时,其他 Bean 注入的是原始对象而非代理,破坏 AOP 功能,且后续无法统一替换为代理(违反单例一致性)

各数据结构的作用

  • singletonObjects(一级缓存,即单例池)。存储完整走完Bean生命周期的bean单例对象。
  • earlySingletonObjects(二级缓存)。存储还未完全走完Bean生命周期的代理对象。用来判断是否存在已经创建的代理对象实例,和获取已经创建的代理对象实例。
  • singleFactories(三级缓存)。仅在真正发生循环依赖且需要早期引用时,才通过工厂调用 getEarlyBeanReference() 决定是否生成代理;生成后立即移入二级缓存防重复,既满足循环依赖解耦,又严守 AOP 时机与单例唯一性 。提前暴露bean的引用,而不是等bean创建后才作为单例暴露出来,减少并发创建bean的进程,提高并发性能;减小锁的粒度。当出现循环依赖时,将需要从bean创建到初始化的全生命周期锁降低为实例化后和完成属性赋值前区间,减少线程并发竞争的概率,提高并发性能;通过二级缓存和一级缓存,保证bean代理对象的唯一性
  • singletonsCurrentlyInCreation(用来判断bean是否在创建过程中,即是否触发循环依赖)。延迟代理的创建时间,并将需要加在全生命周期的锁缩小到三级缓存向二级缓存转换的执行区间

触发循环依赖时会发生AOP提前

循环依赖中的AOP提前现象是指:当出现循环依赖时,会将在初始化过程中的AOP处理过程提前到实例化之后属性赋值之前

spring的实现机制是,首先并不是所有bean都需要做代理,如果没有前置后置等操作没有实现相关processor接口则会直接返回原bean对象。如果需要做AOP提前,bean工厂需要实现SmartInstantiationAwareBeanPostProcessor接口,这样当触发循环依赖时,在生命周期的实例化后属性赋值前,会调用该方法并返回代理后的bean实例以保证生命周期顺利走完

接口设计及实现

先来看看AOP实现类及其接口设计:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {...}

实现了SmartInstantiationAwareBeanPostProcessor接口,这是将需要提前获取代理对象的这种行为作为规范抽象成了接口:

public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
    @Nullable
    default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Nullable
    default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

我们重点关注下getEarlyBeanReference(Object bean, String beanName)方法就行。

当然,这个接口的父接口是经典的后置处理器:

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {...}

所以需要实现这两个方法:

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

说了这么多,其实最主要的方法就只有几个:

// AbstractAutoProxyCreator.class

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    ...

    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return this.wrapIfNecessary(bean, beanName, cacheKey);
    }

    ...

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) { // [17]
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }

        return bean;
    }

    protected Object getCacheKey(Class<?> beanClass, @Nullable String beanName) {
        if (StringUtils.hasLength(beanName)) {
            return FactoryBean.class.isAssignableFrom(beanClass) ? "&" + beanName : beanName;
        } else {
            return beanClass;
        }
    }

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    }

    ...
}
  • getEarlyBeanReference(Object bean, String beanName)。AOP提前时调用的就是这个方法,内部实现和postProcessAfterInitialization(@Nullable Object bean, String beanName)类似。
  • postProcessAfterInitialization(@Nullable Object bean, String beanName)。[17]这里删除了bean在AOP提前时存储的缓存。
  • wrapIfNecessary(Object bean, String beanName, Object cacheKey)。AOP实现逻辑在这个方法中实现。

现在我们只需要了解getEarlyBeanReference和wrapIfNecessary方法的调用时机就能搞懂Spring是如何将AOP提前的了。

AOP提前的实现流程

这里我们基于循环依赖中执行流程的场景事例来模拟AOP的实现过程。

首先在创建AService单例时,基于调用链getBean()->doGetBean()->createBean()->doCreateBean(),快进到这:

    // AbstractAutowireCapableBeanFactory.class

    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
        }

        // 实例化
        if (instanceWrapper == null) {
            instanceWrapper = this.createBeanInstance(beanName, mbd, args);
        }

        Object bean = instanceWrapper.getWrappedInstance();
        ...

        // 判断是否是正在创建的单例Bean
        boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName); // [4.1]
        if (earlySingletonExposure) { // [4.2]
            ...

            // 添加到三级缓存 [4.3]
            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }

        Object exposedObject = bean;

        try {
            // 属性赋值 [4.4]
            this.populateBean(beanName, mbd, instanceWrapper);
            // 初始化
            exposedObject = this.initializeBean(beanName, exposedObject, mbd); // [4.7]
        } catch (Throwable var18) {
            if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
                throw (BeanCreationException)var18;
            }

            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
        }

        if (earlySingletonExposure) { // [4.5]
            Object earlySingletonReference = this.getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
                    ...
                }
            }
        }

        try {
            this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
            return exposedObject; // [4.6]
        } catch (BeanDefinitionValidationException var16) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
        }
    }

[4.3]在三级缓存中添加了AbstractAutowireCapableBeanFactory类getEarlyBeanReference(Object bean, String beanName) 方法的具体实现,我们看看具体实现:

    // AbstractAutowireCapableBeanFactory.class
    
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
            Iterator var5 = this.getBeanPostProcessors().iterator();

            while(var5.hasNext()) {
                BeanPostProcessor bp = (BeanPostProcessor)var5.next();
                // 实现了AOP提前
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }

        return exposedObject;
    }

这里遍历了所有SmartInstantiationAwareBeanPostProcessor接口的实例并调用了getEarlyBeanReference(exposedObject, beanName)方法,这下我们知道了AOP提前操作是在哪行代码实现的。

但是三级缓存中保存的是拉姆达表达式,实际上添加缓存时并没有执行,这时就有两种情况,如果没有循环依赖,那就一直不会执行;如果存在循环依赖,则在触发循环依赖的瞬间才会执行AOP,即创建BService单例时的getBean(BService)->doGetBean()->createBean()->doCreateBean()->populateBean()->getBean(AService)->doGetBean()->getSingleton()

    // DefaultSingletonBeanRegistry.class

    @Nullable
    public Object getSingleton(String beanName) {  // [1.1]处调用的方法
        return this.getSingleton(beanName, true);
    }

    // 当允许循环依赖(allowEarlyReeference)时,会有三种可能性
    // 1. 当一级缓存二级缓存中都没有对应bean,且没有触发循环依赖(即this.isSingletonCurrentlyInCreation),则返回null
    // 2. 当一级缓存二级缓存中都没有对应bean,但是触发了循环依赖时,且三级缓存不为空时,则基于三级缓存的拉姆达表达式生成对应代理对象并放到二级缓存中,并删除三级缓存中对应的项(这里使用了双重检查锁)。
    // 3. 当一级缓存中存在对应的bean,则返回一级缓存
    // 4. 当二级缓存中存在对应的bean,则返回二级缓存
    protected Object getSingleton(String beanName, boolean allowEarlyReference) { // [2]
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { // [2.1]
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) { // [2.2]
                synchronized(this.singletonObjects) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) { // [2.3]
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) { // [2.4]
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) { // [2.5]
                                singletonObject = singletonFactory.getObject(); // [2.6] 
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }

执行到singletonFactory.getObject() [2.6]代码时,才会真正执行AOP操作并返回代理对象。在BService做属性赋值的依赖注入时会直接将AService的代理对象返回。

最后我们看看在创建AService实例的初始化阶段(getBean()->doGetBean()->createBean()->doCreateBean()->initializeBean() [4.7])是如何跳过AOP的。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(() -> {
                this.invokeAwareMethods(beanName, bean);
                return null;
            }, this.getAccessControlContext());
        } else {
            this.invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        }

        try {
            this.invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable var6) {
            throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
        }
        // 初始化后的扩展 
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); // [9.1]
        }

        return wrappedBean;
    }

this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName) [9.1]这个方法实现了AOP操作:

    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;

        Object current;
        for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
            BeanPostProcessor processor = (BeanPostProcessor)var4.next();
            current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
        }

        return result;
    }

这里会调用AOP实现类AbstractAutoProxyCreator的postProcessAfterInitialization方法(前面有分析过),所以这里会直接返回代理对象。至此AOP提前的代码原理已经全部解析完毕。

总结

  • 三级缓存及作用。
    • singletonObjects(一级缓存)。用来存储完整走完Bean生命周期中实例化、属性赋值和初始化三个阶段的单例。一级缓存主要用来缓存单例对象,spring基于它实现了单例模式。
    • earlySingletonObjects(二级缓存)。用来存储还未完全走完Bean生命周期的Bean对象(可能是普通Bean或普通Bean的代理对象)。二级缓存为了保证Bean对象的单例特性,因为最终创建的单例可能是普通Bean对象也可能是普通Bean的代理对象。
    • singleFactories(三级缓存)。用来存储基于bean的拉姆达表达式。
  • 通过缓存和逻辑判断解决了循环依赖问题。
  • 三级缓存的优势。
    • 使bean创建时的逻辑更清晰。
    • 二级三级缓存的存在使加锁粒度更小,提高了并发性能。
    • 第三级缓存存储的拉姆达表达式,具有更高的灵活性。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Press ESC to close