系列二十、Spring循环依赖问题

本文探讨了Spring框架中循环依赖的概念,通过构造方法和set方法注入的实例展示其区别,并介绍了Spring如何利用三级缓存解决单例bean的循环依赖问题。

一、概述

        循环依赖是指多个bean之间相互依赖,形成了一个闭环。比如A依赖于B、B依赖于C、C依赖于A,形成了一个圈,如:

二、循环依赖案例

2.1、构造方法注入产生循环依赖案例

2.1.1、ServiceA

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:14
 * @Description:
 */
@Service
public class ServiceA {

    private ServiceB serviceB;

    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

2.1.2、ServiceB

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:14
 * @Description:
 */
@Service
public class ServiceB {

    private ServiceA serviceA;

    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

2.1.3、MySpringConfig

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:29
 * @Description:
 */
@Configuration
@ComponentScan(basePackages = {"org.star"})
public class MySpringConfig {

}

2.1.4、AopFullAnnotationMainApp

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ServiceA serviceA = context.getBean("serviceA", ServiceA.class);
        log.info("serviceA:{}", serviceA);
    }
}

2.1.5、结论

使用构造方法注入属性会产生循环依赖问题。

2.2、set方法注入不会产生循环依赖案例

2.2.1、ServiceAA

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:27
 * @Description:
 */
@Service
public class ServiceAA {

    @Resource
    private ServiceBB serviceBB;

}

2.2.2、ServiceBB

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/27 9:27
 * @Description:
 */
@Service
public class ServiceBB {

    @Resource
    private ServiceAA serviceAA;

}

2.2.3、MySpringConfig(同上)

2.2.4、AopFullAnnotationMainApp

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/23 15:14
 * @Description:
 */
@Slf4j
public class AopFullAnnotationMainApp {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ServiceAA serviceAA = context.getBean(ServiceAA.class);
        log.info("serviceAA:{}", serviceAA);
    }
}

2.2.5、结论

使用set方法注入属性不会产生循环依赖问题。

2.2.6、注意事项

        默认情况下使用set方法注入属性可以解决循环依赖问题,但是有一个前提,bean是单例的,当把bean设置为多例后,再使用set注入时依然会产生循环依赖问题。

三、Spring解决循环依赖的方法

3.1、三级缓存

        所谓Spring的三级缓存是指Spring内部解决循环依赖问题的三个Map,即 DefaultSingletonBeanRegistry 中定义的三个Map。部分源码如下:

3.2、A、B对象在三级缓存中的迁移过程

第一步:A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B;

第二步:B实例化的时候发现需要A,于是B先查找一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,于是找到A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A;

第三步:B顺利完成初始化,并将自己放到一级缓存里面(注意:此时B里面的A依然是创建中的状态),然后接着回来创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

3.3、断点顺序

# 参考B站尚硅谷周阳老师录制的视频,链接地址如下
 
https://www.bilibili.com/video/BV1Hy4y1B78T?p=39&spm_id_from=pageDriver&vd_source=72ebc194dbfa51ec950a52c25c337f7c

3.4、解决循环依赖的步骤

        第一步:Spring创建bean主要分为两个步骤,即:创建原始bean对象 & 填充对象属性和初始化;

        第二步:每次创建bean之前,Spring都会从缓存中查询该bean是否存在,如果缓存中存在,则直接拿来用,如果没有Spring则去创建原始对象并放入三级缓存中,因为是单例,只能有一个;

        第三步:当创建完A的原始对象后(此时A已经在三级缓存中了),接下来为A填充属性,这时候发现A依赖B,接着又去创建B,同样的流程,创建完B的原始对象后,填充属性时发现B又依赖于A,B实例化的时候需要A,于是B先从一级缓存里面查找A,没有找到,接着再查二级缓存,还是没有,再查三级缓存,于是找到了A,然后Spring把三级缓存里面的A放到二级缓存里面,并删除三级缓存里面的A,B顺利完成初始化,并将自己放入到一级缓存里面(注意:此时B里面的属性A依然是创建中的状态),然后接着回来继续为A填充属性,此时B已经创建结束,Spring直接从一级缓存中取出B,为A赋值,完成A的初始化,然后把A自己放入到一级缓存里面,并删除二级缓存里面的A,此时A,B各自完成实例化。

3.5、解决循环依赖的思想

        Spring解决循环依赖靠的是bean的 中间态 这个概念,而这个中间态指的是 已经实例化但是还没有初始化的状态,即:半成品;实例化的过程又是通过构造器完成的,如果A还没有创建出来怎么提前曝光?这也就解释了使用构造方法注入属性为什么无法解决循环依赖的问题。

Spring解决单例bean循环依赖的三个Map(三级缓存):

        # 一级缓存:单例池,存放已经经历了完整生命周期的bean

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

        # 二级缓存:存放早期暴露出来的bean对象,此时bean的生命周期尚未结束(属性还未填充完毕)

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

        # 三级缓存:存放可以生成bean的工厂

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

3.6、详细过程

【故障诊断】【pytorch】基于CNN-LSTM故障分类的轴承故障诊断研究[西储大学数据](Python代码实现)内容概要:本文介绍了基于CNN-LSTM神经网络模型的轴承故障分类方法,利用PyTorch框架实现,采用西储大学(Case Western Reserve University)公开的轴承故障数据集进行实验验证。该方法结合卷积神经网络(CNN)强大的特征提取能力和长短期记忆网络(LSTM)对时序数据的建模优势,实现对轴承不同故障类型和严重程度的高精度分类。文中详细阐述了数据预处理、模型构建、训练流程及结果分析过程,并提供了完整的Python代码实现,属于典型的工业设备故障诊断领域深度学习应用研究。; 适合人群:具备Python编程基础和深度学习基础知识的高校学生、科研人员及工业界从事设备状态监测与故障诊断的工程师,尤其适合正在开展相关课题研究或希望复现EI级别论文成果的研究者。; 使用场景及目标:① 学习如何使用PyTorch搭建CNN-LSTM混合模型进行时间序列分类;② 掌握轴承振动信号的预处理与特征学习方法;③ 复现并改进基于公开数据集的故障诊断模型,用于学术论文撰写或实际工业场景验证; 阅读建议:建议读者结合提供的代码逐行理解模型实现细节,重点关注数据加载、滑动窗口处理、网络结构设计及训练策略部分,鼓励在原有基础上尝试不同的网络结构或优化算法以提升分类性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值