Spring循环依赖的关键代码和流程示意图


前言

该文章是看视频与书籍,用个人的理解话语完成。所以此文不是很细致,仅是作为关键点记忆。


提示:以下是本篇文章正文内容,内容如有错误,可在评论区回复

一、什么是循环依赖

A对象里,有属性B对象。B对象里,有属性A对象。
A.B对象互相引用,创建时,产生的问题。

public class A {
	private B b;
	
	public B getB() {
		return b;
	}

	public void setB(B b) {
		this.b = b;
	}
	
}

public class B {
	private A a;
	
	public A getA() {
		return a;
	}

	public void setA(A a) {
		this.a = a;
	}
}

二、怎么解决问题

Spring通过提前暴露对象(即完成实例化但未完成初始化的对象)的方式解决循环依赖问题。使用三级缓存,map结构来存储对象。

  1. 先从三级缓存中获取A.发现没有后,创建A对象,进行实例化,生成了半成品A对象
  2. 在进行A对象属性填充前,将A对象的lamdba表达式存入三级缓存。
  3. populateBean设置属性时,需要B对象。容器中没有,开始创建B对象。从新走回第一步,这回创建的是B对象。
  4. B对象实例化后,设置属性前,将B对象的lamdba表达式存入三级缓存
  5. B开始设置属性,需要A对象,这时会从缓存中取A,先从一级缓存,二级缓存,三级缓存来取,三级缓存中有,将执行A的lamdba表达式getEarlyBeanReference,获得A的半成品状态。填充B的属性值。B已经是完整状态。
  6. 然后将A半成品存入二级缓存,将A的lamdba表达式从三级缓存移除。
  7. 将完整的B存入一级缓存,并且删除二,三级缓存中的B。
  8. 可以将B赋值给A对象了,将A存入一级,并且删除二,三级的A。

三级缓存

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	
	/*
	 * 一级缓存
	 */
	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/*
	 * 三级缓存
	 * 用于保存BeanName和创建bean的工厂之间的关系
	 */
	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/*
	  * 二级缓存
	  * 保存BeanName和创建Bean实例之间的关系,与singletonFactories的不同之处在于,当一个单例
	 * Bean被放到这里之后,那么当bean还在创建过程中就可以通过getBean方法获取到,可以方便进行
	  * 循环依赖的检测
	 */
	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
.....
}

三、图解

其中有部分关键代码。可自行打断点调试。
在这里插入图片描述

四、面试题

1、一二三级缓存分别存放什么状态对象?
一级:成品对象二级:半成品三级:lamdba表达式

2、如果只设置一级缓存能否解决循环依赖问题?
如果只有一级缓存,成品对象和半成品都需要存入一级缓存,没办法区分状态。

3、如果只有二级缓存能否解决循环依赖问题?
能解决。但是前提是不使用代理。三级缓存是为了解决代理过程中的循环依赖问题。

思考,那些地方用到了三级缓存?
addSingletonFactory(),向三级缓存存放属性值getSingleton():从三级缓存中获取属性值

4、如果一个对象需要被代理的话,在整个容器中,会存在几个当前对象的版本?
有2个版本
1、原始对象,直接通过反射创建出的对象
2、通过cglib或jdk的动态代理创建对象来的对象

5、到底那里被代理了,当添加了aop之后,跟刚刚的处理步骤哪里不一样?exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);原始bean可能被修改,返回了被代理的对象。

6、总结:每次我们获取对象的时候,是通过对象的name来获取Bean的,如果原始对象和代理对象同时存在的话,那我通过名字在进行获取的时候,应该用哪一个?
无法选择,其实还有最核心的点,你如何能够确认对象时候需要被引用?
使用lamdba表达式其实代表了一种回调机制,当需要使用当前对象的时候,通过lamdba表达式来最终返回一个确定的最终版本对象,而不需要判断有几个对象,因为是替换的过程,所以只能有一个。

7、这三级缓存的查找顺序是啥?
先找singletonObjects,earlySingletonObjects,singletonFactories


总结

这里是完整内容
https://www.processon.com/view/link/62b31877e401fd071e0f6d37


参考内容:

  • 《精通Spring+4.x++企业应用开发实战》
  • B站上的Spring视频
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值