Spring循环依赖问题
什么是循环依赖?
循环依赖就是循环引用,指两个或多个bean互相持有对方,比如说TestA引用TestB、TestB引用TestA,最终形成一个闭环。
注意:循环依赖不是指循环调用。
循环调用:指方法之间的环调用,循环调用是无解的,除非有终结条件,否则就是死循环,最终会导致内存溢出异常。
两种Spring容器循环依赖:
- 构造器循环依赖
- setter方法循环依赖
Spring是如何解决的?
我们先定义循环依赖的实体代码
//bean1
public class TestA {
private TestB testB; TestA(TestB testB){
this.testB=testB;
} public TestB getTestB() {
return testB;
} public void setTestB(TestB testB) {
this.testB = testB;
}
}
//bean2
public class TestB {
private TestA testA; TestB(TestA testA){
this.testA=testA;
} public TestA getTestA() {
return testA;
} public void setTestA(TestA testA) {
this.testA = testA;
}
}
在Spring中循环依赖处理分为3种情况
1. 构造器循环依赖(无法解决)
表示通过构造器注入构成的循环依赖,此依赖是无解的,强行依赖只能抛出异常(BeanCreationException);
Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因此在创建bean的过程中如果发现自己已经在池中,则抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的bean将从“当前创建bean池”中清除掉。
下面我们通过一段代码来印证上述理论。
创建application.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="testA" class="com.chenpt.springFrameWork.TestA">
<constructor-arg name="testB" ref="testB"></constructor-arg>
</bean> <bean id="testB" class="com.chenpt.springFrameWork.TestB">
<constructor-arg name="testA" ref="testA"></constructor-arg>
</bean> </beans>
客户端测试用例:
public class MainTest {
public static void main(String[] args){
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring/applicationContext.xml"); }
}
执行结果如下图(仅截了部分错误)
2. setter循环依赖(可以解决)
指通过setter注入方式构成的循环依赖。
解决方式:Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的bean来完成的。而且只能解决单例作用域的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean。
代码示例:
首先需要去掉构造器注入的参数。
//bean1
public class TestA {
private TestB testB; TestA(){} public TestB getTestB() {
return testB;
} public void setTestB(TestB testB) {
this.testB = testB;
}
}
//bean2
public class TestB {
private TestA testA; TestB(){} public TestA getTestA() {
return testA;
} public void setTestA(TestA testA) {
this.testA = testA;
}
}
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="testA" class="com.chenpt.springFrameWork.TestA" scope="singleton">
<property name="testB" ref="testB"/>
</bean> <bean id="testB" class="com.chenpt.springFrameWork.TestB" scope="singleton">
<property name="testA" ref="testA"/>
</bean> </beans>
客户端执行(自行演示,无输出错误)
3. prototype范围的依赖处理(无法解决)
对于prototype作用域bean,spring容器无法完成依赖注入,因为spring容器不进行缓存prototype作用域的bean,因此无法提前暴露一个正在创建中的bean。
示例代码如下:
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="testA" class="com.chenpt.springFrameWork.TestA" scope="prototype">
<property name="testB" ref="testB"/>
</bean> <bean id="testB" class="com.chenpt.springFrameWork.TestB" scope="prototype">
<property name="testA" ref="testA"/>
</bean> </beans>
客户端示例
public class MainTest {
public static void main(String[] args){
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:spring/applicationContext.xml"); TestB testB = context.getBean("testB",TestB.class);
}
}
执行结果,抛出异常
针对上述的作用域(scope)分析
beanFactory除了拥有ioc的职责外,还有着对象生命周期管理。
scope用来声明容器中对象所应该处的限定场景或者该对象的存活时间,即容器在对象进入其相应的scope之前,生成并装配这些对象,在对象不再处于scope的限定之后,容器通常会销毁这些对象。
spring容器提供了几种scope类型?
- singleton:在spring容器中只存在一个实例,所有对象的引用将共享这个实例。(注:不要和单例模式搞混)
- prototype:容器每次都会生成一个新的对象实例给请求方。
- request (限定在web应用中使用):为每个http请求创建一个全新的request-processor对象供当前请求使用,请求结束,实例生命周期即结束。
- session (限定在web应用中使用):为每个独立的session创建一个全新的UserPreference对象实例。
- global session (限定在web应用中使用):只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的session。如果在普通的基于servlet的Web应用中使用了这个类型的scope,容器会将其作为普通的session类型的scope对待。
参考:《Spring源码深度解析》
《Spring揭秘》
Spring循环依赖问题的更多相关文章
- spring循环依赖问题分析
新搞了一个单点登录的项目,用的cas,要把源码的cas-webapp改造成适合我们业务场景的项目,于是新加了一些spring的配置文件. 但是在项目启动时报错了,错误日志如下: 一月 , :: 下午 ...
- Spring 循环依赖
循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环.此处不 ...
- Springboot源码分析之Spring循环依赖揭秘
摘要: 若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效.或许刚说到这,有的小伙伴就会大惊失色了.Spring不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我 ...
- Spring 循环依赖的三种方式(三级缓存解决Set循环依赖问题)
本篇文章解决以下问题: [1] . Spring循环依赖指的是什么? [2] . Spring能解决哪种情况的循环依赖?不能解决哪种情况? [3] . Spring能解决的循环依赖原理(三级缓存) 一 ...
- Spring循环依赖的解决
## Spring循环依赖的解决 ### 什么是循环依赖 循环依赖,是依赖关系形成了一个圆环.比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A.那么这时候A和B ...
- 这个 Spring 循环依赖的坑,90% 以上的人都不知道
1. 前言 这两天工作遇到了一个挺有意思的Spring循环依赖的问题,但是这个和以往遇到的循环依赖问题都不太一样,隐藏的相当隐蔽,网络上也很少看到有其他人遇到类似的问题.这里权且称他非典型Spring ...
- Spring — 循环依赖
读完这篇文章你将会收获到 Spring 循环依赖可以分为哪两种 Spring 如何解决 setter 循环依赖 Spring 为何是三级缓存 , 二级不行 ? Spring 为啥不能解决构造器循环依赖 ...
- 帮助你更好的理解Spring循环依赖
网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...
- spring 循环依赖的一次 理解
前言: 在看spring 循环依赖的问题中,知道原理,网上一堆的资料有讲原理. 但今天在看代码过程中,又产生了疑问. 疑问点如下: // 疑问点: 先进行 dependon 判断String[] de ...
- 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖
本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...
随机推荐
- 记录一个bootstrap惨痛的错误
记录一个bootstrap的错误,这个错误因为我删除了一个class就导致了页面上显示的错误,这是一个惨痛的教训,特此记录,提醒自己在做前端的修改时,一定要慎之又慎.如果真的要做改动,改完之后也要测一 ...
- xcode10对应的xcode command line tool编译的坑
众所周知,xcode10新增的编译系统new build system会不支持一些老项目的编译,一般的做法是在Xcode编译配置Xcode->File->Project Settings/ ...
- UML第三次作业
一.PlantUML类图 语法学习小结 关系上的标识:在关系之间使用标签来说明时, 使用 :后接 标签文字.对元素的说明,可以在每一边使用 "" 来说明. 1 @startuml ...
- ESP8266清理flash学习记录
学习来源:http://bbs.eeworld.com.cn/thread-497588-1-1.html 还稍看了电子产品世界 主要内容 1在windows 上通过 命令行 安装 Python环境 ...
- PHP yii框架FormWidget组件
本篇文章介绍的是PHP yii框架Form组件,方便在view层更好调用此功能,话不多说上代码:1.先继承yii本身Widget类 <?php/** * User: lsh */ namespa ...
- CentOS7系统 ansible自动化部署多台服务器部署
CentOS7系统 ansible自动化部署多台服务器部署 Ansible工作机制 从图中可以看出ansible分为以下几个部份: 1> Control Node:控制机器2> In ...
- hibernate一级缓存及对象的状态
hibernate中实体类对象的状态 在hibernate中实体类对象有三种状态 (1)瞬时态(临时态) 瞬时态:即我们自己创建一个对象,还没有保存到数据库就叫临时态,其实也可以说是对像没有id值,跟 ...
- CentOS 7 安装samba服务
STEP 1. 安装 #安装 [root@study ~]yum install smaba [root@study ~]systemctl start smb nmb STEP 2. 建立共享目录以 ...
- platform怎么实现数据数据和驱动分离
一些重要的结构体: struct platform_device { const char * name; int id; struct device dev; u32 num_resources; ...
- c++算法实现(一) - 递归和初始化
递归 写递归函数经常出错,提醒自己两个规则: 1. 一般来说递归函数中不应该出现for.while之类的循环语句, 因为递归就是循环的另外一种实现: 2. 注意基线条件,具体参考<算法图解> ...