spring 循环依赖的一次 理解
前言:
// 疑问点: 先进行 dependon 判断
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
} // Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
这里先简单记录下 现在理解的 循环依赖的大致流程:
1、depende-on 标签的情况
<bean id="aService" class="com.zzf.spring.dependent.AService" depends-on="bService"/>
<bean id="bService" class="com.zzf.spring.dependent.BService" depends-on="aService"/>
注: depends-on适用于表面上看起来两个bean之间没有使用属性之类的强连接的bean,但是两个bean又确实存在前后依赖关系的情况,使用了depends-on的时候,依赖他人的bean是先于被依赖bean销毁的。 一般不会这么使用。
也就是这样配置的情况,才会抛出 BeanCreationException 异常。
if (isDependent(beanName, dep)) {// 判断返回 true, 抛出循环依赖的exception
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
2、正确的 xml 配置的循环依赖 demo
<bean id="aService" class="com.zzf.spring.dependent.AService">
<property name="bService" ref="bService"/>
</bean>
<bean id="bService" class="com.zzf.spring.dependent.BService">
<property name="aService" ref="aService"/>
</bean>
3、注解的方式解决循环依赖
@Service
public class Aservice {
@Autowired
private BService service;
} @Service
public class BService {
@Autowired
private Aservice aservice;
}
都说这段是 解决 循环依赖的 关键所在:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
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;
}
这里主要涉及到3个缓存, singletonObjects,earlySingletonObjects, singletonFactories。
- singletonObjects: 单例对象的 cache
- singletonFactories: 单例对象工厂的 cache
- earlySingletonObjects: 提前曝光的单例对象的 cache。(这是关键)
这里只考虑 A--B --A的情况:
Object sharedInstance = getSingleton(beanName);
第一次 getBean(A)的时候, 返回是 null, 会走如下流程:
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
在 createBean -> doCreate中有如下: addSingletonFactory() 提前 曝光当前类 工厂,到 singletonFactory中
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");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
然后执行 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 进行 属性的 赋值。 在进行 B的属性赋值中, 发现B还没有初始化, 则会去 调用 BeadFactory.getBean(B) 进行 B的初始化。
调用B的过程中,和A类似, 也会 提前 在singletonFactory 中曝光, 然后在 populateBean 中,注入 A属性值时, 因为A未初始化,再次 去请求 getBean(A), 这次 在 getSingleton()中,因为 A提前曝光,所以在getSingleton 中 返回 A(可能未完全初始化),最终调用 getObjectForBeanInstance 方法,返回 完全实例话的 bean A, 然后注入到B 中,并完成B的 初始化, bean都会 放进singletonObjects 缓存中。
TODO: 在 populateBean 中怎么检测 到 properties,这块还需 仔细的去debug,还没完全理清楚。
参考资料:http://cmsblogs.com/?p=2887,看了多次,总结的很好。
spring 循环依赖的一次 理解的更多相关文章
- 帮助你更好的理解Spring循环依赖
网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...
- Spring循环依赖的解决
## Spring循环依赖的解决 ### 什么是循环依赖 循环依赖,是依赖关系形成了一个圆环.比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A.那么这时候A和B ...
- 这个 Spring 循环依赖的坑,90% 以上的人都不知道
1. 前言 这两天工作遇到了一个挺有意思的Spring循环依赖的问题,但是这个和以往遇到的循环依赖问题都不太一样,隐藏的相当隐蔽,网络上也很少看到有其他人遇到类似的问题.这里权且称他非典型Spring ...
- Spring — 循环依赖
读完这篇文章你将会收获到 Spring 循环依赖可以分为哪两种 Spring 如何解决 setter 循环依赖 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能解决的循环依赖原理(三级缓存) 一 ...
- 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖
本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...
随机推荐
- int c, int ndigit[10]; ++ndigit[c-'0'];
for example c-'0' is an integer expression with a value between 0and 9 corresponding to the characte ...
- Isset、empty、count、is_null的比较
1.empty判断变量是否为空, 先把变量转为布尔值再返回:对变量(字符串.数组等)赋值为一切为空的值.或者未定义的变量都返回true,即判断为空,比如null,' ',0,array(),false ...
- springSecurity使用
目录 1.1 依赖包导入 2.1 没有安全机制下的一个web 3.1 做一个安全机制的web 官网https://spring.io/guides/gs/securing-web/ 无情的翻译官... ...
- MySQL 事务 异常 事务隔离的级别
MySQL 事务 异常 事务隔离的级别 事务 在你操作数据库的同时,有可能其他用户还会不断地对数据进行增删改查操作.为了避免并行进行时出现混乱,就产生了"事务".事务就是要保证 ...
- Xenon's Attack on the Gangs(树规)
题干 Input Output Example Test 1: Test 2: 3 5 1 2 1 2 2 3 1 3 1 4 3 5 3 10 Tips 译成人话 给n个结点,n-1条无向边.即一棵 ...
- Spring 5.2.x 源码环境搭建(Windows 系统环境下)
前期准备 1.确保本机已经安装好了 Git 2.Jdk 版本至少为 1.8 3.安装好 IntelliJ IDEA (其他开发工具,如 eclipse.Spring Tool Suite 等也是可以的 ...
- Layui数据表格动态加载操作按钮
效果: 方法一:绑定模版选择器 <div class="layui-card"> <div class="layui-card-body layui-r ...
- 清空网站浏览记录就行啦?看Python如何实时监控网站浏览记录
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 需求: (1) 获取你对象chrome前一天的浏览记录中的所有网址(url ...
- Redux的理解与使用
一.什么是Redux? Redux是一个第三方状态管理的js库,它不仅仅可以适用于react框架,还可以用于其他的vue,auglar等框架.只不过react的生态中不包括一个状态管理的库而已,所以与 ...
- CCNA-Part5 - 传输层 ,TCP 为什么是三次握手?
传输层 传输层主要的作用就是建立端到端的连接.比如电脑的微信的通信,就需要跨越多个网络设备(交换机和录取)再和微信的服务器建立连接. 传输层需要具有以下的特点: 会话的多复用:如电脑上开启的多个应用, ...