Spring循环依赖的三种方式

引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错。下面说一下Spring是如果解决循环依赖的。

第一种:构造器参数循环依赖

Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持
在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出
BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。

首先我们先初始化三个Bean。

public class StudentA {private StudentB studentB ;public void setStudentB(StudentB studentB) {this.studentB = studentB;}public StudentA() {}public StudentA(StudentB studentB) {this.studentB = studentB;}
}
public class StudentB {private StudentC studentC ;public void setStudentC(StudentC studentC) {this.studentC = studentC;}public StudentB() {}public StudentB(StudentC studentC) {this.studentC = studentC;}
}
public class StudentC {private StudentA studentA ;public void setStudentA(StudentA studentA) {this.studentA = studentA;}public StudentC() {}public StudentC(StudentA studentA) {this.studentA = studentA;}
}

OK,上面是很基本的3个类,,StudentA有参构造是StudentB。StudentB的有参构造是StudentC,StudentC的有参构造是StudentA ,这样就产生了一个循环依赖的情况,

我们都把这三个Bean交给Spring管理,并用有参构造实例化

    <bean id="a" class="com.zfx.student.StudentA"><constructor-arg index="0" ref="b"></constructor-arg></bean><bean id="b" class="com.zfx.student.StudentB"><constructor-arg index="0" ref="c"></constructor-arg></bean><bean id="c" class="com.zfx.student.StudentC"><constructor-arg index="0" ref="a"></constructor-arg></bean> 

下面是测试类:

public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");//System.out.println(context.getBean("a", StudentA.class));}
}

执行结果报错信息为:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

如果大家理解开头那句话的话,这个报错应该不惊讶,Spring容器先创建单例StudentA,StudentA依赖StudentB,然后将A放在“当前创建Bean池”中,此时创建StudentB,StudentB依赖StudentC ,然后将B放在“当前创建Bean池”中,此时创建StudentC,StudentC又依赖StudentA, 但是,此时Student已经在池中,所以会报错,,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,(初始化完的Bean会从池中移除)

第二种:setter方式单例,默认方式

如果要说setter方式注入的话,我们最好先看一张Spring中Bean实例化的图

img

如图中前两步骤得知:Spring是先将Bean对象实例化之后再设置对象属性的

修改配置文件为set方式注入

	<!--scope="singleton"(默认就是单例方式)  --><bean id="a" class="com.zfx.student.StudentA" scope="singleton"><property name="studentB" ref="b"></property></bean><bean id="b" class="com.zfx.student.StudentB" scope="singleton"><property name="studentC" ref="c"></property></bean><bean id="c" class="com.zfx.student.StudentC" scope="singleton"><property name="studentA" ref="a"></property></bean>

下面是测试类:

public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");System.out.println(context.getBean("a", StudentA.class));}
}

打印结果为:

com.zfx.student.StudentA@1fbfd6

为什么用set方式就不报错了呢 ?

我们结合上面那张图看,Spring先是用构造实例化Bean对象 ,此时Spring会将这个实例化结束的对象放到一个Map中,并且Spring提供了获取这个未设置属性的实例化对象引用的方法。 结合我们的实例来看,,当Spring实例化了StudentA、StudentB、StudentC后,紧接着会去设置对象的属性,此时StudentA依赖StudentB,就会去Map中取出存在里面的单例StudentB对象,以此类推,不会出来循环的问题喽、

下面是Spring源码中的实现方法,。以下的源码在Spring的Bean包中的DefaultSingletonBeanRegistry.java类中

/** Cache of singleton objects: bean name --> bean instance(缓存单例实例化对象的Map集合) */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);/** Cache of singleton factories: bean name --> ObjectFactory(单例的工厂Bean缓存集合) */private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);/** Cache of early singleton objects: bean name --> bean instance(早期的单身对象缓存集合) */private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);/** Set of registered singletons, containing the bean names in registration order(单例的实例化对象名称集合) */private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);/*** 添加单例实例* 解决循环引用的问题* Add the given singleton factory for building the specified singleton* if necessary.* <p>To be called for eager registration of singletons, e.g. to be able to* resolve circular references.* @param beanName the name of the bean* @param singletonFactory the factory for the singleton object*/protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}

第三种:setter方式原型,prototype

修改配置文件为:

    <bean id="a" class="com.zfx.student.StudentA" scope="prototype"><property name="studentB" ref="b"></property></bean><bean id="b" class="com.zfx.student.StudentB" scope="prototype"><property name="studentC" ref="c"></property></bean><bean id="c" class="com.zfx.student.StudentC" scope="prototype"><property name="studentA" ref="a"></property></bean>

scope=“prototype” 意思是 每次请求都会创建一个实例对象。两者的区别是:有状态的bean都使用Prototype作用域,无状态的一般都使用singleton单例作用域。

测试用例:

public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");//此时必须要获取Spring管理的实例,因为现在scope="prototype" 只有请求获取的时候才会实例化对象System.out.println(context.getBean("a", StudentA.class));}
}

打印结果:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

为什么原型模式就报错了呢 ?

对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556501.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring 是如何解决循环依赖的?

1.由同事抛的一个问题开始 最近项目组的一个同事遇到了一个问题&#xff0c;问我的意见&#xff0c;一下子引起的我的兴趣&#xff0c;因为这个问题我也是第一次遇到。平时自认为对spring循环依赖问题还是比较了解的&#xff0c;直到遇到这个和后面的几个问题后&#xff0c;重…

java按钮触发另一个页面_前端跨页面通信,你知道哪些方法?

戳蓝字「前端技术优选」关注我们哦&#xff01; 引言在浏览器中&#xff0c;我们可以同时打开多个Tab页&#xff0c;每个Tab页可以粗略理解为一个“独立”的运行环境&#xff0c;即使是全局对象也不会在多个Tab间共享。然而有些时候&#xff0c;我们希望能在这些“独立”的Tab页…

【Java用法】java 8两个List集合取交集、并集、差集、去重并集

在业务的开发过程中会经常用到两个List集合相互取值的情况&#xff0c;于是记录在此&#xff0c;方便后续使用哦~~~ public class ListTest {public static void main(String[] args) {ArrayList<String> listA CollectionUtil.toList("a", "b", &…

jsonp react 获取返回值_Django+React全栈开发:文章列表

React现在我们有了一个属于文章的API&#xff0c;可以添加、修改、删除、查看文章&#xff0c;但是对于我们的网站来说&#xff0c;还需要一个用户界面才行。现在开始探索一下ReactJS吧。经常听到有前端三大框架Angular、React、Vue的说法&#xff0c;不过React官网对自己的介绍…

24个经典的MySQL索引问题,你都遇到过哪些?

1、什么是索引&#xff1f; 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分)&#xff0c;它们包含着对数据表里所有记录的引用指针。 索引是一种数据结构。数据库索引&#xff0c;是数据库管理系统中一个排序的数据结构&#xff0c;以协助快速查询、更新数…

java 3 4_Java-3/4_树.md at master · yrcDream/Java-3 · GitHub

树二叉树二叉树具有唯一根节点二叉树每个节点最多有两个孩子&#xff0c;最多有一个父亲二叉树具有天然递归结构二叉树不一定是 “满” 的&#xff1a;一个节点也是二叉树、空节点也是二叉树二叉搜索树(BST)BST 的基本功能public class BST> {private Node root;private int…

python模块导入_python模块导入

不同的执行方式&#xff1a; 从IDE中执行&#xff0c;python程序由IDE设置环境决定。 从系统中执行&#xff0c;python程序由环境变量中的系统变量path决定&#xff0c;从上往下选择。 模块导入顺序&#xff1a; 系统包优先级最高 > 同目录 > sys.path&#xff0c;之所以…

再也不怕SVN冲突:轻松解决SVN冲突

什么时候容易出现冲突&#xff1f; 多个人同时修改了同个文件中的同一行代码 无法进行对比的二进制文件&#xff0c;比如图片等 如何解决冲突&#xff1f; 如上图&#xff0c;test_conflict.py文件发生了冲突&#xff0c;并且多出了几个文件&#xff0c;其中.mine是我本地修…

手机型号大全_2020值得入手的三款手机。每个优秀,选择哪一个?励志故事名言视频...

如今&#xff0c;手机等数码产品更新很快。各种新的技能&#xff0c;让用户真正体验到科技的力量&#xff0c;它可以被描述为“具有多种功能的一个装置。”然而&#xff0c;这么多车型&#xff0c;难免有些人不知道如何选择。当4G和5G手机**的对峙&#xff0c;很多朋友也问小中…

AspectJ

Aspectj与Spring AOP比较 XML配置方式 <aop:aspect>&#xff1a; 定义切面, 包括通知和切点. 是一般的bean//定义切面 public class SleepHelperAspect{public void beforeSleep(){System.out.println("睡觉前要脱衣服&#xff01;");}public void afterSleep…

aixs1 生成java代码_通过axis1.4 来生成java客户端代码

1.首先下载axis-1.4所有的jar包&#xff0c;2.我是直接打开cmd&#xff0c;进入到该jar包的目录下&#xff0c;3.直接运行命令(运行这个命令之前要确定java的环境变量都已配置好)&#xff1a;java -Djava.ext.dirs${lib的目录} org.apache.axis.wsdl.WSDL2Java -o${代码输出路径…

windows分屏_windows内到底藏了多少好东西?

恭喜!点开这篇文章&#xff0c;你将解锁 WIN10 系统内那些不为人知的高效的冷知识&#xff01;相信所有的职场人都会搜索过这样的问题&#xff1a;有哪些高效的办公神器&#xff1f;在之前的文章中&#xff0c;我分享过很多高效神器&#xff0c;如果你感兴趣的话&#xff0c;点…

@Aspect中@Pointcut 12种用法

本文主要内容&#xff1a;掌握Pointcut的12种用法。 Aop相关阅读 阅读本文之前&#xff0c;需要先掌握下面3篇文章内容&#xff0c;不然会比较吃力。 Spring系列第15篇&#xff1a;代理详解&#xff08;java动态代理&CGLIB代理)Spring系列第30篇&#xff1a;jdk动态代理…

asp.net接受表单验证格式后再提交数据_看滴普科技大前端如何玩转el-form-renderer 表单渲染器1.14.0

DEEPEXI 大前端常人道&#xff0c;一入开发深似海&#xff0c;技术学习无止境。在新技术层出不穷的前端开发领域&#xff0c;有一群身怀绝技的开发&#xff0c;他们在钻研前沿技术的同时&#xff0c;也不忘分享他们的成果&#xff0c;回馈社区。下面&#xff0c;就由小水滴带大…

测试用例设计方法_黑盒测试——测试用例设计方法

黑盒测试也称为功能测试或数据驱动测试。通过软件的外部表现来发现其缺陷和错误。在测试时&#xff0c;把被测程序视为一个不能打开的盒子&#xff0c;在完全不考虑程序内部逻辑结构和内部特性的情况下进行。它是在已知产品所应具有的功能前提下&#xff0c;通过测试来检测每个…

SpringAop @Pointcut(“@annotation“)\@Aspect练习

切面记录日志 切面类 Slf4j Aspect Component public class AspectForFeign {Pointcut("execution(public * com.keke.remote..*Feign.*(..))")public void pointcut() {}Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) thro…

Mybatis缓存机制详解与实例分析

前言&#xff1a; 本篇文章主要讲解Mybatis缓存机制的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601; 以下正文开始 Mybat…

delphi语言转为汇编语言_每天5分钟,轻松建立技术图谱 编程语言黑历史

阿T课堂开播啦&#xff01;这里只有干货干锅&#xff0c;没有水坑没有套路&#xff01;计算机编程语言的发展&#xff0c;也是随着计算机本身发展而发展。人类不断的提高科技的同时&#xff0c;也必须使工具的使用越来越简化&#xff0c;从而提高整个社会效率&#xff0c;这其中…

水系图一般在哪里找得到_进展 | 水系钠离子电池研究取得重要进展

水系钠离子电池兼具钠资源储量丰富和水系电解液本质安全的双重优势被视为一种理想的大规模静态储能技术。此前&#xff0c;我们针对这水系钠离子电池体系做了一些探索(Nature Communications 2015, 6, 6401&#xff1b;Advanced Energy Materials 2015, 5, 1501005&#xff1b;…

@Around简单使用示例——SpringAOP增强处理

Around的作用 既可以在目标方法之前织入增强动作&#xff0c;也可以在执行目标方法之后织入增强动作&#xff1b;可以决定目标方法在什么时候执行&#xff0c;如何执行&#xff0c;甚至可以完全阻止目标目标方法的执行&#xff1b;可以改变执行目标方法的参数值&#xff0c;也…