Spring解决循环依赖的思路

 2022-09-15
原文地址:https://cloud.tencent.com/developer/article/1691673

Spring解决循环依赖的思路

一. 什么是循环依赖

循环依赖也就是循环引用,指两个或多个对象互相持有对方的引用。通俗地说,假设在Spring中有3个Service Bean,分别为ServiceA、ServiceB和ServiceC,如果ServiceA引用了ServiceB,ServiceB引用了ServiceC,而ServiceC又引用了ServiceA,最终形成可一个环,这样就出现了循环依赖。

二. Spring如何解决循环依赖

对Spring来说循环依赖,有以下几种:

  1. Prototype类型Bean的循环依赖
  2. 构造器循环依赖
  3. setter循环依赖

对于第1类和第2类的循环依赖,Spring的处理是不解决,直接抛出BeanCurrentlyInCreationException异常。

因此,Spring只处理Singleton类型的Bean的setter循环依赖 。其处理思路大概为:对于循环依赖的Bean,提前暴露一个单例工厂ObjectFactory,从而使得其他Bean能引用到该Bean。以前面所说的三个Service为例,具体步骤如下:

  1. Spring 容器创建单例ServiceA,首先根据无参构造器创建Bean,并提前暴露一个 “ObjectFactory”,表示一个创建中的Bean,并将ServiceA放到“当前正在创建的Bean池”中,然后进行 setter注入ServiceB。
  2. ServiceB通过同样的方式,暴露一个ObjectFactory,并将ServiceB放入“当前正在创建的Bean池”中,然后进行setter注入ServiceC。
  3. ServiceC也通过同样的方式,暴露ObjectFactory并将Bean入池,接下来在注入ServiceA的时候,发现ServiceA处于位于正在创建池中,说明出现了循环依赖,此时由于ServiceA已经提前暴露了一个ObjectFactory,则ServiceC会注入ServiceA工厂返回的正在创建中的对象。
  4. 最后通过setter注入ServiceB和ServiceA,完成依赖创建过程。

三. 源码分析

说了这么多理论,下面来分析下源码。我们使用Spring时的一个基本操作就是Bean的获取,那么我们从AbstractBeanFactory的getBean()方法入手:

    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

getBean()只是一个接口方法,核心逻辑在doGetBean()中:

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                              @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
        //解析beanName
        final String beanName = transformedBeanName(name);
        Object bean;
    
        //直接从缓存中或者singletonFactories中对应的ObjectFactory获取
        //检查缓存中或者实例工厂中是否有对应的实例,这样处理是为了解决单例Bean循环依赖的问题
        //在创建单例Bean的过程中会存在依赖注入的情况,而在依赖注入过程中,为了避免循环依赖,Spring的处理是:
        //不等Bean创建完成,就提前曝光创建Bean的ObjectFactory,也就是将ObjectFactory加入到缓存中,
        //一旦一个Bean创建时需要依赖上一个Bean,则直接从缓存中获取ObjectFactory
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isTraceEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                                 "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            //返回对应的实例,因为从缓存中获取的Object未必是Bean本身,可能是FactoryBean之类的,需要调用这个方法获取真正的Bean
            //有时会返回Bean指定方法所创建的对象而不是Bean本身,如FactoryBean
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
    
        else {
            //Spring只处理Singleton类型Bean的循环依赖
            //对于Prototype类型如果存在循环依赖,直接抛出BeanCurrentlyInCreationException
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
    
            //如果当前容器的BeanDefinition列表不存在Bean的定义,且父容器不为空,则尝试从parentBeanFactory中加载Bean
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                        nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else if (requiredType != null) {
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                else {
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }
    
            //如果不仅仅是做类型检查,则是要创建Bean,则先将Bean标记为已创建状态
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }
    
            try {
                //获取Bean的定义——RootBeanDefinition
                //如果指定的Bean是子Bean的话,则同时会合并父Bean的属性
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);
    
                //如果存在依赖的Bean,则递归实例化所有依赖的Bean
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                            "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        //缓存依赖调用
                        registerDependentBean(dep, beanName);
                        try {
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                            "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
    
                //所有依赖Bean已经实例化完成,开始实例化自身
    
                //SingletonBean的创建
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
    
                //Prototype的创建
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
    
                //其他Scope类型Bean的创建
                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, () -> {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                                        "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                                        "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                                        ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }
    
        //类型检查:如果传入的requiredType不为空,则对创建好的Bean进行类型校验,如果不满足类型则抛出BeanNotOfRequiredTypeException
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return convertedBean;
            }
            catch (TypeMismatchException ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Failed to convert bean '" + name + "' to required type '" +
                                 ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }

代码的核心部分我已经加了注释。在获取Bean时,首先会调用getSingleton()方法,因为Spring会将所有Singleton类型的Bean缓存起来,因此首先尝试从缓存中获取。而循环依赖的处理也在这个方法中。点进去看一下:

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //首先从singletonObject缓存中获取,如果存在直接返回
        Object singletonObject = this.singletonObjects.get(beanName);
    
        //如果缓存中不存在,则检查singletonsCurrentlyInCreation,是否该beanName正在创建
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    
            //如果singletonsCurrentlyInCreation中存在beanName,说明该Bean正在创建
            //即出现了循环依赖的情况
            synchronized (this.singletonObjects) {
                //尝试从earlySingletonObjects中获取Bean
                singletonObject = this.earlySingletonObjects.get(beanName);
    
                //如果earlySingletonObjects中也不存在,且允许提前暴露Bean,则从singletonFactories中获取Bean对应的ObjectFactory
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    
                    //如果Bean对应的ObjectFactory存在,则使用ObjectFactory创建Bean,将Bean加入到earlySingletonObjects中
                    //并且从singletonFactories中移除该singletonFactory
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

刚方法主要做了以下处理:

  1. 首先尝试从singletonObjects缓存中获取Bean,如果获取到了,说明Bean已经创建完成,直接返回即可。
  2. 缓存中不存在,则检查是否该Bean正在创建,这是解决循环依赖的关键。Spring通过singletonsCurrentlyInCreation这个Set保存了所有正在创建中的beanName。如果在创建一个Bean时,在singletonsCurrentlyInCreation找到了这个Bean的name,则说明出现了循环依赖。
  3. 首先尝试从earlySingletonObjects中获取暴露的创建中的对象,如果不存在,再尝试从singletonFactories中获取提前暴露的对象工程BeanFactory,并调用其getObject()方法,实例化一个提前暴露的正在创建中的对象并放入earlySingletonObjects,然后返回这个创建中的对象。

可以看到,Spring解决循环依赖的方式就是"提前暴露法",在循环引用时,引用提前暴露的正在创建中的对象而非真正实例化完成的对象。

那么singletonFactory中的BeanFactory是在何时创建的呢?搜索调用链,可以看到在AbstractAutowireCapableBeanFactory的doCreateBean()方法中:

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {
    
        //实例化Bean,大部分情况都是调用其无参构造器
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }
    
        //允许MergedBeanDefinitionPostProcessor修改BeanDefinition
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                    "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }
    
        //将刚刚实例化完成的Bean,以ObjectFactory的形式放入singletonFactories中,以解决循环依赖的问题
        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");
            }
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
    
        ...
    }

可以看到,在创建Bean的过程中,一旦实例化完成,就将Bean以ObjectFactory的形式放入singletonFactories中,以解决循环依赖的问题。此时的Bean还未进行属性的赋值和依赖的注入,就是一个空壳。

而在Bean创建完成后,会将其对应的ObjectFactory移除,代码如下:

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

四. 总结

至此,Spring的循环依赖解决思路基本描述完成。可以看到,整体的处理方式还是很巧妙的。主要流程总结如下:

  1. 创建Bean过程中,将beanName放入singletonsCurrentlyInCreation正在创建池中,并创建对应的ObjectFactory放入对象工厂缓存池。
  2. 解析Bean依赖过程中,如果发现存在了循环依赖,则直接引用ObjectFactory所创建的提前暴露的Bean。
  3. Bean的创建结束后,将其从singletonsCurrentlyInCreation中移除,并删除对应的ObjectFactory。

整个过程中,Spring对缓存的处理也很巧妙,现将常用的缓存总结如下(DefaultSingletonBeanRegistry类中):

    /**
    	 * 用于保存所有已创建的Singleton Bean
    	 * key:beanName
    	 * value:Singleton Bean
    	 */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /**
    	 * 用于保存创建Bean对应的ObjectFactory工厂
    	 * key:beanName
    	 * value:Bean对应的ObjectFactory
    	 */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    /**
    	 * 用于保存提前暴露出的Bean,与singletonObjects不同的是,这里的Bean可能还未创建完成,但是通过getBean()也能够获取到,主要是为了解决循环依赖的问题
    	 * 当Bean创建完成后,会从该缓存中移除
    	 * key:beanName
    	 * value:提前暴露出的Bean
    	 */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    
    /**
    	 * 用于保存所有已经注册号的beanName
    	 */
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);