当前位置: 首页 > article >正文

简单说说 spring 是如何处理循环依赖问题的(源码解析)

聊聊源码

在spring 中,解决循环依赖的关键是三级缓存,缓存数据在 DefaultSingletonBeanRegistry类中

/** Cache of singleton objects: bean name to bean instance. */
//一级缓存,是最终生成的对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存,和aop相关
//ObjectFactory 是一个函数式接口,如果有进行aop,则返回aop对象,否则返回的是原始对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存,完成实例化,未完成初始化的对象
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

1 提前暴露

AbstractAutowireCapableBeanFactory#doCreateBean

在这个方法中,spring会将已经完成实例化的bean放入到三级缓存中去。

//AbstractAutowireCapableBeanFactory 类
//创建bean的过程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    //如果是单例,先从缓存中进行获取,如果获取的到,则直接使用
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    //如果缓存中没有,则创建bean实例
    if (instanceWrapper == null) {
        //【核心】创建实例
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    .... 其他处理逻辑
    /******************************* 1 提前暴露 *******************************/
    // 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.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
        }
        //这个方法会将当前的实例化完成,初始化未完的对象,存储到 singletonFactories 缓存中去
        //之所以存到的是三级缓存,是因为对象可能是经过aop的,在singletonFactories中存储的是ObjectFactory
        // ObjectFactory是一个函数式接口,如果对象有经过aop会返回aop对象,否则返回原对象
        //singletonFactories 就是我们所说的三级缓存
        //同时,会将beanName 存入 registeredSingletons 列表中,作用是提前曝光,是解决循环袭来的关键
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        //【核心】填充bean的属性
        //1 完成属性的注入,根据bean定义的时候,设置的属性值。
        //2 解析依赖 如果bean依赖其他的bean,则其会递归的去创建其他的bean
        //3 调用BeanPostProcessor,这里主要是执行 InstantiationAwareBeanPostProcessor
        populateBean(beanName, mbd, instanceWrapper);
        //【核心】初始化bean
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    ... 其他处理逻辑

    return exposedObject;
}

2 从缓存中依次获取bean

在populateBean中,会去循环遍历需要进行依赖注入的bean,会通过getBean方法去进行获取,会走到DefaultSingletonBeanRegistry.getSingleton

过程如下

1 从一级缓存中获取,如果可以获取到,直接返回

2 如果一级缓存中获取不到,从二级缓存中获取,如果获取到,则直接返回

3 如果二级缓存也获取不到,则从三级缓存中获取

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// Quick check for existing instance without full singleton lock
    //先从一级缓存中获取,一级缓存存放的是成品bean,就是已经完成了实例化和初始化
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        //从二级缓存中获取(早期单例缓存),只完成了实例化的bean
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                //这里进行二重检查
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // 从三级缓存中获取,三级缓存存放的是ObjectFactory,
                        // ObjectFactory是一个函数式接口,这个方法就是 
                        // AbstractAutowireCapableBeanFactory.getEarlyBeanReference
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

三级缓存中存储的对象是ObjectFactory,这个对象定义的是一个函数式接口,最终会调用回设置的位置,就是

AbstractAutowireCapableBeanFactory的getEarlyBeanReference方法

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                //这是一个BeanPosterProcessor,是一个接口
                //1 允许获取一个早期的bean
                //2 一般框架中,比如mybatis中的事务,会实现这个接口的getEarlyBeanReference方法
                //然后返回一个aop之后的类。
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

总结

从源码中,我们可以推断出,循环依赖的处理有几个基础的点,分别是

1 三级缓存,三级缓存中的第三级缓存是处理循环依赖的关键

2 提前暴露,在bean完成实例化之后,spring就会将还未创建完成的bean暴露到三级缓存中。

3 允许获取仅实例化的bean进行赋值。

需要注意的是:spring中,只能实现set注入的循环依赖,如果在构造器中产生循环依赖,spring无法解决

传送阵

简单说说 spring构造器循环依赖 为什么无法解决(源码解析)-CSDN博客


http://www.kler.cn/a/354712.html

相关文章:

  • ChatGPT网络错误如何解决
  • 【实用技能】如何使用 .NET C# 中的 Azure Key Vault 中的 PFX 证书对 PDF 文档进行签名
  • Docker Compose 启动 Harbor 并指定网络
  • Three.js - 打开Web 3D世界的大门
  • Pytorch学习12_最大池化的使用
  • Flink系统知识讲解之:如何识别反压的源头
  • Unity 2D角色的跳跃与二段跳示例
  • Springboot 整合 Java DL4J 实现物流仓库货物分类
  • 论文翻译 | LARGE LANGUAGE MODELS ARE HUMAN-LEVELPROMPT ENGINEERS
  • 计算机网络自顶向下(4)---应用层HTTP协议
  • Nginx在Windows Server下的启动脚本
  • 20201017-【C、C++】跳动的爱心
  • Git推送被拒
  • exists在sql中的妙用
  • Linux笔记---vim的使用
  • OpenHarmony 入门——ArkUI 自定义组件内同步的装饰器@State小结(二)
  • vue使用gdal-async获取tif文件的缩略图
  • 【系统架构设计师】案例分析考点情况分析和解题技巧(包括2009~2024年考点详情)
  • 详解UDP-TCP网络编程
  • 【C#生态园】提升数据处理效率:C#中多款数据清洗库全面解析
  • 【wpf】07 后端验证及令牌码获取步骤
  • [旧日谈]关于Qt的刷新事件频率,以及我们在Qt的框架上做实时的绘制操作时我们该关心什么。
  • 关于FFmpeg【使用方法、常见问题、解决方案等】
  • jmeter 对 dubbo 接口测试是怎么实现的?有哪几个步骤
  • 我谈结构自相似性SSIM——实质度量的是什么?
  • JavaScript 小技巧和诀窍:助你写出更简洁高效的代码