乐趣区

Spring-bean的循环依赖以及解决方式

什么是循环依赖

循环依赖其实就是循环引用,也就是两个或则两个以上的 bean 互相持有对方,最终形成闭环。比如 A 依赖于 B,B 依赖于 C,C 又依赖于 A,由于对象之间的依赖关系造成了死循环。

Spring 中循环依赖场景有:

  • 构造器的循环依赖
  • field 属性的循环依赖

Spring 怎么解决循环依赖

Spring 循环依赖主要基于 Java 引用传递,当获取到对象时,对象的 field 或者属性可以延后设置

创建 Bean

    /**
     * Actually create the specified bean. Pre-creation processing has already happened
     * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
     * <p>Differentiates between default bean instantiation, use of a
     * factory method, and autowiring a constructor.
     * @param beanName the name of the bean
     * @param mbd the merged bean definition for the bean
     * @param args explicit arguments to use for constructor or factory method invocation
     * @return a new instance of the bean
     * @throws BeanCreationException if the bean could not be created
     * @see #instantiateBean
     * @see #instantiateUsingFactoryMethod
     * @see #autowireConstructor
     * 实际创建指定的 bean。预创建处理已经发生
     * 在这一点上,例如 检查 {@code postProcessBeforeInstantiation} 回调。* <p> 区分默认的 bean 实例化,使用
     * 工厂方法,并自动装配构造函数。* @param beanName bean 的名称
     * @param mbd 该 bean 的合并 bean 定义
     * @param args 用于构造函数或工厂方法调用的显式参数
     * @返回 bean 的新实例
     * @throws BeanCreationException 如果无法创建 bean
     * @请参阅 #instantiateBean
     * @see #instantiateUsingFactoryMethod
     * @请参阅 #autowireConstructor
     */
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
             // 从缓存中移除 bean,因为要创建新的 bean 了
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
              // 根据指定 bean 的使用策略创建新的实例,如:工厂方法,构造函数自动注入,简单初始化
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}

        // Allow post-processors to modify the merged bean definition.
        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;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
         // 是否需要提早曝光:单例 & 允许循环依赖 & 当前 bean 正在创建中,检测循环依赖
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean'" + beanName +
                        "'to allow for resolving potential circular references");
            }
             // 为避免后期循环依赖,可以在 bean 初始化完成前将创建实例的 ObjectFactory 加入工厂,其他 bean 引用此 bean 时直接从 ObjectFactory 获取 bean
             //getEarlyBeanReference 对 bean 再一次依赖引用,主要应用 SmartInstantiationAware BeanPost Processor 其中我们熟悉的 AOP 就是在这里将 advance 动态织入 bean 中的,如果没有就直接返回 bean,不做任何处理
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
             // 对 bean 进行填充, 将各个属性值注入,其中,可能存在依赖于其他 bean 的属性,则会递归给初始依赖 bean
            populateBean(beanName, mbd, instanceWrapper);
             // 调用初始化方法,如 init-method
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

        if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);
             //earlySingletonReference 只有在检测到有依赖循环的情况下才会不为空
            if (earlySingletonReference != null) {
                  // 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强
                if (exposedObject == bean) {exposedObject = earlySingletonReference;}
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                          // 检测依赖
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);
                        }
                    }
                      // 因为 bean 创建后其所依赖的 bean 一定是已经创建的,actualDependentBeans 不为空则表示当前 bean 创建后其依赖的 bean 却没有创建完,也就是存在循环依赖
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name'" + beanName + "'has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been" +
                                "wrapped. This means that said other beans do not use the final version of the" +
                                "bean. This is often the result of over-eager type matching - consider using" +
                                "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

        // Register bean as disposable.
        try {
              // 根据 scopse 注册 bean
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

以上是 Spring 创建 bean 的方法,代码很多但是核心思路就是 实例化,填充属性,初始化,而循环依赖主要发生在实例化和填充属性这两部分

        // 根据指定 bean 的使用策略创建新的实例,如:工厂方法,构造函数自动注入,简单初始化
        instanceWrapper = createBeanInstance(beanName, mbd, args);
        // 对 bean 进行填充, 将各个属性值注入,其中,可能存在依赖于其他 bean 的属性,则会递归给初始依赖 bean
        populateBean(beanName, mbd, instanceWrapper);
        // 调用初始化方法,如 init-method
        exposedObject = initializeBean(beanName, exposedObject, mbd);

Spring 为了解决循环依赖,设置了三级缓存

一级缓存 singletonObjects

用于保存 BeanName 和创建 bean 实例之间的关系,beanName -> bean instance

private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

二级缓存 earlySingletonObjects

保存提前曝光的单例 bean 对象

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存 singletonFactories

保存 beanName 和创建 bean 实例之间的关系, 与 singletonObjects 不同的地方在于,当一个单例 bean 被放到这里面后,bean 在创建过程中,可以通过 getBean 方法获取到, 目的是用来检测循环引用

private final Map<String, Object> singletonFactories = new HashMap(16);

在创建 bean 的时候,首先从缓存中获取单例的 bean,这个缓存就是 singletonObjects,如果获取不到且 bean 正在创建中,就再从earlySingletonObjects 中获取,如果还是获取不到且允许从 singletonFactories 中通过 getObject 拿到对象,就从 singletonFactories 中获取,如果获取到了就存入 earlySingletonObjects 并从 singletonFactories 中移除。

getSingleton

    // 用于保存 BeanName 和创建 bean 实例之间的关系,beanName -> bean instance
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    // 保存提前曝光的单例 bean 对象
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    // 保存 beanName 和创建 bean 实例之间的关系, 与 singletonObjects 不同的地方在于,当一个单例 bean 被放到这里面后,bean 在创建过程中,可以通过 getBean 方法获取到, 目的是用来检测循环引用
    private final Map<String, Object> singletonFactories = new HashMap(16);
    // 保存当前所有已注册的 bean
    private final Set<String> registeredSingletons = new LinkedHashSet(256);

    /**
     * Return the (raw) singleton object registered under the given name.
     * <p>Checks already instantiated singletons and also allows for an early
     * reference to a currently created singleton (resolving a circular reference).
     * @param beanName the name of the bean to look for
     * @param allowEarlyReference whether early references should be created or not
     * @return the registered singleton object, or {@code null} if none found
     * 返回以给定名称注册的(原始)单例对象。* <p> 检查已实例化的单例,并允许早期
     * 引用当前创建的单例(解析循环引用)。* @param beanName bean 的名称
     * @param allowEarlyReference 是否允许从 singletonFactories 中通过 getObject 拿到对象
     * @返回注册的单例对象;如果找不到,则返回{@code null}
     */
    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
         // 检查缓存中是否存在实例
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
             // 如果为空, 且正在创建中, 锁定全局变量并进行处理
            synchronized (this.singletonObjects) {
                  // 如果 bean 正在加载则不处理
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                      // 当某些方法需要提前初始化的时候会调用 addSingletonFactory 方法将对于 ObjectFactory 初始化策略存储在 singletonFactories
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                          // 调用预设的 getObject 方法
                        singletonObject = singletonFactory.getObject();
                          // 记录在缓存中 earlySingletonObjects 和 singletonFactories 互斥
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

addSingletonFactory

    /**
     * Add the given singleton factory for building the specified singleton
     * if necessary.
     * <p>To be called for eager registration of singletons, e.g. to be able to
     * resolve circular references.
     * @param beanName the name of the bean
     * @param singletonFactory the factory for the singleton object
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

现在有 A 的 field 或者 setter 依赖 B 的实例对象,同时 B 的 field 或者 setter 依赖了 A 的实例,A 首先开始创建,并将自己暴露到 singletonFactories 中,开始填充属性,此时发现自己依赖 B 的属性,尝试去 get(B),发现 B 还没有被创建,所以开始创建 B,在进行属性填充时初始化 A,就从 singletonObjects 中获取到了初始化但没有任何属性的 A,B 拿到 A 后完成了初始化阶段,将自己放到 singletonObjects 中, 此时返回 A,A 拿到 B 的对象继续完成初始化,完成后将自己放到 singletonObjects 中,由 A 与 B 中所表示的 A 的属性地址是一样的,所以 A 的属性填充完后,B 也获取了 A 的属性,这样就解决了循环的问题。

退出移动版