SPRING12_循环依赖、三级缓存源码分析、循环引用图解、解释必须有三级缓存

①. 什么是循环依赖?

  • ①. 在spring中,对象的创建是交给Spring容器去执行的,Spring创建的Bean默认是单例的,也就是说,在整个Spring容器中,每一个Bean对象都是有且只有一个

  • ②. 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方。
    比如A引用B,B引用A,这是循环依赖最直接、最简单的一个例子,不管循环依赖是两个对象之间,还是三个对象、四个对象、五个对象等等,最后产生了循环依赖的原因都是如此

@Component
public class A {
  @Autowired
  private B b;
  public void setB(B b) {
    this.b = b;
  }
}
@Component
public class B {
  @Autowired
  private A a;
  public void setA(A a) {
    this.a = a;
  }
}
  • ③. 当前这种情况形成了一个闭环,即产生了循环依赖问题
    在这里插入图片描述

②. 谈谈三级缓存到底是什么?

  • ①. 一级缓存:用于存储被完整创建了的bean。也就是完成了初始化之后,可以直接被其它对象使用的bean

  • ②. 二级缓存:用于存储半成品的Bean。也就是刚实例化但是还没有进行初始化的Bean

  • ③. 三级缓存:三级缓存存储的是工厂对象(lambda表达式)。工厂对象可以产生Bean对象提前暴露的引用半成品的Bean或者半成品的代理Bean对象),执行这个lambda表达式,就会将引用放入三级缓存中

DefaultSingletonBeanRegistry/** Cache of singleton objects: bean name to bean instance. */
// 一级缓存:存储完整的单例对象。key是对象名称,value是对象实例。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */
// 二级缓存:存储实例化后的对象对象未注入属性和初始化。key是对象名称,value是对象实例。
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

/** Cache of singleton factories: bean name to ObjectFactory. */
// 三级缓存:存储生成单例对象的工厂对象。key是对象名称,value是工厂实例。
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  • ④. 一级缓存和二级缓存的返回结果中Map的参数都是Object,而三级缓存的是ObjectFactory<?>。ObjectFactory也就是"函数式接口",作用就是"可以把匿名内部类和lambda表达式传递进去当做参数,调用的时候通过getObject方法来调用"

  • ⑤. spring汇总有三个缓存,用于存储单例的bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。如果调用getBean,则需要从三个缓存中依次获取指定的Bean实例。读取顺序依次是一级缓存 --> 二级缓存 --> 三级缓存
    在这里插入图片描述

③. 三级缓存debug流程的总结

  • ①. 通过反射获取构造器并实例化对象a,把lambda表达式对象工厂当做value存进三级缓存的map中,完成对象a的实例化
  1. 这里的lambda表达式就是把实际对象bean交给代理对象,然后判断是否有AOP,如果有就直接对代理对象进行修改
  2. 如果没有就不修改直接跳过,最终返回那个代理对象也就是说如果没有AOP的话,只是进行了一个对象代理,其他什么也没做
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {

	// Instantiate the bean.
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {//是否单例
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		// 创建Bean的实列,默认使用无参构造创建对象
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	Object bean = instanceWrapper.getWrappedInstance();
	.....
	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));
		}
	}
  • ②. 完成对象a的实例化后,接着开始对象a的属性填充,遍历属性并且通过解析判断发现依赖对象b,先从容器的三个缓存中依次查找对象b,没有找到,于是开始调用getBean方法创建对象b
// 给创建好的对象每个属性进行赋值,@Autowired发生在这里
populateBean(beanName, mbd, instanceWrapper);
  • ③. 通过反射获取构造器并实例化对象b,把lambda表达式对象工厂当做value存进三级缓存的map中,完成对象b的实例化。此时三级缓存里有【a对象工厂】和【b对象工厂】

  • ④. 完成对象b的实例化后,接着开始对象b的属性填充,遍历属性并且通过解析判断发现依赖对象a,先从容器的三个缓存中依次查找对象a,在三级缓存里找到了对象a的对象工厂lambda表达式,于是开始通过getObject方法调用前面传到value里的lambda表达式来创建一个单例对象a,完成对象a创建后把对象a放进二级缓存,并且删除三级缓存里的a对象工厂

  1. 此时三级缓存里有【b对象工厂】,二级缓存里有【半成品对象A{b=null}】
  2. 删除了三级缓存里的【a对象工厂】
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 先检查单列缓存池,获取当前对象 Quick check for existing instance without full singleton lock
	Object singletonObject = this.singletonObjects.get(beanName);
	// 先看单例池有没有 并且 是否正在创建
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 如果当前bean正在创建过程中,而且缓存中没有继续往下
		// earlySingletonObjects:早期单例池
		singletonObject = this.earlySingletonObjects.get(beanName);
		if (singletonObject == null && allowEarlyReference) {
			synchronized (this.singletonObjects) {
				// Consistent creation of early reference within full singleton lock
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					singletonObject = this.earlySingletonObjects.get(beanName);
					if (singletonObject == null) {
						// singletonFactories:单例工厂池
						ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
						if (singletonFactory != null) {
							singletonObject = singletonFactory.getObject();
							this.earlySingletonObjects.put(beanName, singletonObject);
							this.singletonFactories.remove(beanName);
						}
					}
				}
			}
		}
	}
	return singletonObject;
}
  • ⑤. 取到半成品对象a后继续到对象b的属性填充,b对象完成属性填充和初始化,B{a=null}变成B{a=@xxx},把对象b放进一级缓存,并且删除二级缓存和三级缓存中的b对象
  1. 此时三级缓存里啥也没有了,二级缓存里有【半成品对象A{b=null}】,一级缓存里有【成品对象B{a=@***}】
  2. 删除了三级缓存里的【b对象工厂】
  • ⑥. 取到成品对象b后继续到对象a的属性填充,a对象完成属性填充和初始化,A{b=null}变成A{b=@xxx},把对象a放进一级缓存,并且删除二级缓存和三级缓存中的a对象
  1. 此时三级缓存里啥也没有了,二级缓存里啥也没有了,一级缓存里有【成品对象B{a=@***}】 和【成品对象A{b=@—}】
  2. 删除了二级缓存里的【半成品对象A{b=null}】

④. 超详细循环引用图

在这里插入图片描述

⑤. 是否可以只要两级缓存解决?

  • ①. 拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时B注入的是一个半成品的实例A对象,不过随着B初始化完成后,A会继续进行后续的初始化操作,最终B会注入的是一个完整的A实例,因为在内存中它们是同一个对象。下面是重点,我们发现这个二级缓存好像显得有点多余,好像可以去掉,只需要一级和三级缓存也可以做到解决循环依赖的问题???

  • ②. 只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,下面来看一下bean被AOP进行了切面代理的场景

在这里插入图片描述

  • ③. 我们发现AService的testAopProxy被AOP代理了,看看传入的匿名内部类的getEarlyBeanReference返回的是什么对象
    在这里插入图片描述

  • ④. 发现singletonFactory.getObject()返回的是一个AService的代理对象,还是被CGLIB代理的。再看一张再执行一遍singletonFactory.getObject()返回的是否是同一个AService的代理对象
    在这里插入图片描述

  • ⑤. 我们会发现再执行一遍singleFactory.getObject()方法又是一个新的代理对象,这就会有问题了,因为AService是单例的,每次执行singleFactory.getObject()方法又会产生新的代理对象,假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到singleFactory对象,执行getObject()方法又会产生新的代理对象,这是不行的,因为AService是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行了singleFactory.getObject()产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍singletonFactory.getObject()方法再产生一个新的代理对象,保证始终只有一个代理对象。

  • ⑥. 所以如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

所得皆惊喜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值