【Spring】循环依赖

@

循环依赖

是什么?

​ 简单的来说就是对象a的属性中引用了对象b,对象b的属性中引用了对象c......最后引用到a。

在这里插入图片描述

<bean id="a" class="com.zmm.test.A" lazy-init="false">
    <property name="b" ref="b"/>
</bean>

<bean id="b" class="com.zmm.test.B"  lazy-init="false">
    <property name="c" ref="c"/>
</bean>

<bean id="c" class="com.zmm.test.C"  lazy-init="false">
    <property name="a" ref="a"/>
</bean>

Spring是如何解决的?

解决方法

  1. 三级缓存(其实二级缓存也能解决,只是看是否使用AOP)
// DefaultSingletonBeanRegistry类下的

/**
 *						一级缓存
 *			用来存放成品bean对象(已经成功实例化与初始化了的)
 *
 * Cache of singleton objects: bean name to bean instance.
 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
 *						三级缓存 (AOP的关键,如果不用AOP,二级缓存也能解决循环依赖)
 * 		用来存放早期的bean实例(lambda表达式,一个匿名内部类的形式),看bean对象是否需要被代理。
 * 		ObjectFactory<?>是一个函数式接口,
 *
 * Cache of singleton factories: bean name to ObjectFactory.
 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/**
 *						二级缓存
 * 		用来存放半成品bean对象,已经实例化了的但是未初始化的bean对象
 *
 * Cache of early singleton objects: bean name to bean instance.
 */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  1. 提前暴露

AbstractAutowireCapableBeanFactory类的doCreateBean()方法

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
    // .......

    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    // 急于缓存单例,以便即使在诸如BeanFactoryAware之类的生命周期接口触发时也能够解析循环引用。

    // 是否是单例的 && 允许循环引用 && 是Singleton当前bean正在创作中
    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");
        }
        // 一个匿名内部类,提前暴露创建的实例bean。可以防止循环引用,尽早的持有对象的引用
        // 如果一级缓存中不存在指定的bean,就添加到三级缓存中去,将二级缓存中的移除
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // ........................

    // 如果是提前暴露的单例
    if (earlySingletonExposure) {
        // 获取指定名称的已注册的单例模式的Bean对象
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            // 如果获取到的bean对象不为空
            if (exposedObject == bean) {
                // 如果获取到的Bean对象与当前实例化的Bean对象相同
                // 将实例暴露出去,当前实例初始化完成
                exposedObject = earlySingletonReference;
            }
            // 当前Bean依赖其他Bean,并且当发生循环引用时不允许创建新的实例对象
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                // 获取当前Bean所依赖的其他Bean
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                // .......
            }
        }
    }

    // ......

    return exposedObject;
}

BeanDefinitionValueResolver类下的resolveValueIfNecessary()方法

// 解析属性值,对注入类型进行转换
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    // 对引用类型的属性进行解析
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        // 调用引用类型的解析方法
        return resolveReference(argName, ref);
    }
    
    // ......对其他类型的属性的解析
}

大致流程

在这里插入图片描述

源码分析

​ A引用B、B引用C、C引用A。根据上面大致流程来。

​ 在docreate()方法中,先对a实例化
在这里插入图片描述
在这里插入图片描述

将a实例的引用暴露出去

在这里插入图片描述

在这里插入图片描述

准备去填充属性,进入populateBean()方法,applyPropertyValues()方法继续进入,重点来了,resolveValueIfNecessary()方法

在这里插入图片描述

继续进入,我们会看到会调用resolveReference()这个方法

在这里插入图片描述

最终又会调用getBean()方法
在这里插入图片描述

接下来我就省略对b的实例化,直接去看对c的。
在这里插入图片描述

继续跟着上面的走,在BeanDefinitionValueResolver类的resolveReference()方法时,调用getBean()去获取了a的实例
在这里插入图片描述

从缓存中获取a的实例

在这里插入图片描述

我们继续跟进到c实例的doGetBean()方法

在这里插入图片描述

这时候,c的实例化初始化已经完成了,然后就是b的初始化以及a的初始化了,步骤类似,自行去debug吧。

细节

1、在三级缓存中,实例的变更情况。例如a、b、c。序号代表顺序

一级缓存:7.添加c、9.添加b、11.添加a

二级缓存:4.添加a、10.移除a

三级缓存:1.添加a、2.添加b、3.添加c、5.移除a、6.移除c、8.移除b

2、关于使用构造器注入和set注入,下面是官网的解释

posted @ 2021-04-24 09:56  糯米糍好吃!  阅读(71)  评论(0编辑  收藏  举报