Java OpenResty Spring Spring Boot MySQL Redis MongoDB PostgreSQL Linux Android Nginx 面试 小程序 Arthas JVM AQS juc Kubernetes Docker 诊断工具


Spring 循环依赖

Spring 面试 评论 1 大约 7564 字

原因

ClassA的构造中需要ClassBClassB的构造中需要ClassA

public class ClassA {

    public ClassA(ClassB classB) {
        System.out.println("class a constructor");
    }
}

public class ClassB {

    public ClassB(ClassA classA) {
        System.out.println("class b constructor");
    }
}

Spring 官网描述

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependency-resolution

Circular dependencies If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).

Spring官网构造循环依赖无法解决,推荐使用setter方法。但多例情况下也会抛出异常(默认SpringBean是单例的)。

异常信息

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:219)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
    ... 28 more

Spring 解决方案

三级缓存

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
}

singletonObjects

一级缓存,存放经历了完整生命周期的Bean对象。

earlySingletonObjects

二级缓存,存放实例化完成但初始化还没完成的Bean对象(属性未初始化)。

singletonFactories

三级缓存,存放ObjectFactory接口类型的对象(匿名内部类)。实例化完成的Bean也作为参数传入了该接口内的方法中。

获取单例步骤

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    // isSingletonCurrentlyInCreation 判断是否正在创建中,可能在填充属性阶段有其他依赖需要注入
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 二级缓存中有没有
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 为空 且 允许提前引用
            if (singletonObject == null && allowEarlyReference) {
                // 三级缓存中有没有需要被调用的
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 拿出三级缓存中的实例
                    singletonObject = singletonFactory.getObject();
                    // 放入二级缓存中
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 清除三级缓存
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

Bean 生命周期

Bean创建经历的生命周期

doGetBean

先从getSingleton(beanName, true)三级缓存中获取实例,没有则进入getSingleton(String beanName, ObjectFactory<?> singletonFactory)步骤。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        // 转换别名等操作获取 beanName 
        String beanName = transformedBeanName(name);
        Object bean;
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            // 三级缓存中获取实例
            bean = sharedInstance; // 省略了 FactoryBean 判断逻辑,假设 Bean 没有实现 FactoryBean
        } else {
            // 获取 Bean 定义信息
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                // 如果是单例就创建
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        // getSingleton 中调用
                        return createBean(beanName, mbd, args);
                    });
                    bean = sharedInstance; // 省略了 FactoryBean 判断逻辑,假设 Bean 没有实现 FactoryBean
                }
        }
        return (T)bean;
    }
}

getSingleton

先从三级缓存中获取实例,没有则进入singletonFactory.getObject()调用的createBean方法,实际进入doCreateBean步骤。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // 添加标志位表示正在创建
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                try {
                    // 获得到实例,调用的是 doGetBean 传入的 createBean 方法
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } finally {
                    // 清除标志位
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    // 添加到一级缓存中,并删除二级、三级缓存
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }
}

doCreateBean

  • createBeanInstance首先实例化Bean对象。
  • 再调用addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));加入到第三级缓存中。
  • populateBean填充成员变量等属性,成员变量包含了未创建的对象则递归创建Bean对象。如果依赖了未完整初始化的对象,则将未完整出初始化的对象从三级缓存中加入到二级缓存
  • initializeBean回调Bean实现的Aware接口和后置处理器

addSigleton

getSingleton中如果Bean对象创建成功,则newSingletontrue,进入addSigleton,将对象添加到一级缓存中,并删除二级、三级缓存。

示例图

Circular dependencies.svg

示例图链接:https://www.processon.com/view/link/5fd19adee401fd06ddb82c01

备注

Spring Boot 2.6.0默认开闭了循环依赖,Spring官方不提倡使用循环依赖注入方式,希望开发者修改代码。

通过配置可开启:

spring.main.allow-circular-references=true
阅读 1671 · 发布于 2021-12-22

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
  • fHLvlxbf 1楼
    e
    Chrome | Windows 10 2023-07-24
随便看看 换一批