循环依赖

简介: 《读尽源码》

循环依赖

一个对象依赖对象闭环到自己

A -> B -> .... ->A

tip:

不涉及代理对象问题

解决方法:当一个对象已经实例化完毕了,还未初始化的时候,将它注入到它所依赖的已经实例好的对象(提前暴露对象),使得它所依赖的对象是个完整对象(实例化+初始化),然后再将这个完整对象注入给它。

简单工程(Spring-version-5.3.18)

我们就用下面两个类进行实践,多个类间依赖也是如此。

A 类

package cn.demo1;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class A {
    private B b;
}Copy to clipboardErrorCopied

B 类

package cn.demo1;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class B {
    private A a;
}Copy to clipboardErrorCopied

配置文件 test1.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="a" class="cn.demo1.A">
        <property name="b" ref="b"/>
    </bean>
    <bean id="b" class="cn.demo1.B">
        <property name="a" ref="a"/>
    </bean>
</beans>Copy to clipboardErrorCopied

DefaultSingletonBeanRegistry 类中的几个特别重要的属性

// 一级缓存      存放完整Bean对象(实例化+初始化)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 三级缓存      存放一个lambda表达式
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 二级缓存      存放一个半成品bean对象(只是实例化还未初始化),提前暴露
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);Copy to clipboardErrorCopied

循环依赖问题应该是出现属性填充的时候

doCreateBean 这个方法

可以参照 createBeanInstance 查看 Spring 是怎么实例化的

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
    // bean的包装类
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // 就只是bean的实例化
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }
    // 一般为true
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
    // ....省略部分
    if (earlySingletonExposure) {
     // 这里是将一段lambda放入三级缓存中,可以看见bean填充属性之前会将三级缓存创建好,它传入了一个还未初始化的bean
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    // ..........省略部分
    return exposedObject;
}Copy to clipboardErrorCopied

addSingletonFactory

// 其中singletonFactory是一个lambda表达式
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        // 如果我们一级缓存中不存在这个叫beanName的bean
        if (!this.singletonObjects.containsKey(beanName)) {
            // 放入三级缓存中
            this.singletonFactories.put(beanName, singletonFactory);
            // 把二级缓存中叫beanName的半成品bean删除
            this.earlySingletonObjects.remove(beanName);
            // 标记当前注册的bean
            this.registeredSingletons.add(beanName);
        }
    }
}Copy to clipboardErrorCopied

lambda 所执行的方法

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    // 普通bean是进不来的
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    // 直接返回传进来的bean,返回的是一个还未初始化的bean,是提前暴露的
    return exposedObject;
}Copy to clipboardErrorCopied

populateBean 中有调用了 applyPropertyValues 这个方法具体详情请点击这里 applyProertyValues

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    // Create a deep copy, resolving any references for values.
    List<PropertyValue> deepCopy = new ArrayList<>(original.size());
    boolean resolveNecessary = false;
    for (PropertyValue pv : original) {
        if (pv.isConverted()) {
            deepCopy.add(pv);
        }
        else {
            // 属性名字
            String propertyName = pv.getName();
            //当你引用另一个bean的时候,会把它封装成RuntimeBeanReference这个对象,便于操作
            Object originalValue = pv.getValue();
            // 这里是解析的工作,也就是会产生循环依赖产生的地方
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
            // 省略....
        }
}Copy to clipboardErrorCopied

applyPropertyValues 中有个重要的方法调用,省略无关代码

// 我们当前需要要的就是
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    // 当前bean的属性值的类型正是这个
   if (value instanceof RuntimeBeanReference) {
      RuntimeBeanReference ref = (RuntimeBeanReference) value;
      return resolveReference(argName, ref);
   }
    // 省略...
}Copy to clipboardErrorCopied

resolveReferance 中有一段代码

// 这个方法会调用getBean
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
    // 省略...
    // 上面一般进不去,直接看这个重点
    resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
    // 获取所依赖的bean
    bean = this.beanFactory.getBean(resolvedName);
    // 省略...
}Copy to clipboardErrorCopied

getBean 从而到这个 doGetBean 方法,其他代码不多说,最主要是下面这个

其中有一段代码,首先它会尝试从缓存中获取到 bean,如果获取不到就创建这个 bean

Object sharedInstance = getSingleton(beanName);Copy to clipboardErrorCopied

获取缓存 bean 的顺序是,先从一级缓存中取,若不存在,从二级缓存中取,若还是不存在,则从三级缓存中取

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 一级缓存中是否存在
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果想要获取的bean正在创建中且无一级缓存
    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) {
                            // 调用三级缓存,这个地方就会调用我们的lambda表达式了
                            /**
                             *() -> getEarlyBeanReference(beanName, mbd, bean)
                             *这里就是我们解决办法的地方,因为所有普通的bean会首先提前进行三级缓存
                             *所以这里会获取到还未初始化的bean,从而赋值到所依赖当前singletonObject对象的bean
                             */
                            singletonObject = singletonFactory.getObject();
                            // 放入二级缓存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 三级缓存中移除当前beanName的lambda表达式
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    // 完整对象或者还未初始化的对象
    return singletonObject;
}Copy to clipboardErrorCopied

最后经历这个就获取到一个半成品对象所依赖的一个完整对象,然后再将完整对象注入半成品对象中。

相关文章
|
7月前
|
设计模式 测试技术 编译器
C++项目中打破循环依赖的锁链:实用方法大全(一)
C++项目中打破循环依赖的锁链:实用方法大全
758 0
|
5月前
|
传感器
循环依赖问题之Bean循环依赖的定义是什么
循环依赖问题之Bean循环依赖的定义是什么
循环依赖问题之Bean循环依赖的定义是什么
|
5月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决
|
5月前
|
存储 缓存 Java
Spring循环依赖问题之循环依赖异常如何解决
Spring循环依赖问题之循环依赖异常如何解决
|
7月前
|
缓存 Java 网络架构
从简入繁介绍springbean的循环依赖
【4月更文挑战第11天】Spring Bean的循环依赖是Spring框架中一个比较复杂且有趣的话题,涉及到Spring的核心容器、Bean生命周期和依赖注入机制。我将从简入繁地介绍这一概念,确保即使是初学者也能理解。
58 1
|
7月前
|
Java 测试技术 Spring
什么是循环依赖,如何解决?
在 Spring 应用中,循环依赖指的是两个或多个 Bean 之间相互引用,造成了一个环状的依赖关系。举例来说,如果 Bean A 依赖于 Bean B,同时 Bean B 也依赖于 Bean A,就形成了循环依赖。这种情况下,Spring 容器在创建这些 Bean 时会陷入无限循环,导致应用启动失败或者出现其他不可预测的问题。
122 1
|
7月前
|
设计模式 敏捷开发 持续交付
C++项目中打破循环依赖的锁链:实用方法大全(三)
C++项目中打破循环依赖的锁链:实用方法大全
248 0
|
7月前
|
测试技术 持续交付 API
C++项目中打破循环依赖的锁链:实用方法大全(二)
C++项目中打破循环依赖的锁链:实用方法大全
231 0
|
7月前
|
Java 测试技术 Spring
什么是循环依赖,如何解决
什么是循环依赖,如何解决
|
7月前
|
Java 测试技术 Spring
什么是循环依赖,并如何解决
在 Spring 应用中,循环依赖指的是两个或多个 Bean 之间相互引用,造成了一个环状的依赖关系。举例来说,如果 Bean A 依赖于 Bean B,同时 Bean B 也依赖于 Bean A,就形成了循环依赖。这种情况下,Spring 容器在创建这些 Bean 时会陷入无限循环,导致应用启动失败或者出现其他不可预测的问题。
137 0