Spring的三级缓存解决循环依赖

一、三级缓存解决循环依赖流程

假设要获取BeanA(BeanA依赖注入了BeanB,BeanB中依赖注入了BeanA)

1.获取Bean入口
  • getBean()进入,其中的doGetBean中调用getSingleton(name,boolean);
2.getSingleton获取Bean
  • 各级缓存中获取为空说明Bean还没创建,后面继续getSingleton(String beanName, ObjectFactory<?> singletonFactory),singletonFactory是一个lambada表达式。
	@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object bean;
		//省略无关代码
		
		// 首先通过这个方法获取,若获取为空,下面调用另一个getSingleton获取
		Object sharedInstance = getSingleton(beanName);

		if (mbd.isSingleton()) {
			sharedInstance = getSingleton(beanName, () -> {
				try {
					return createBean(beanName, mbd, args);
				}
				catch (BeansException ex) {
					destroySingleton(beanName);
					throw ex;
				}
			});
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
		//省略无关代码
		
		return (T) bean;
3.之前Bean没有被创建过,获取Bean为空,开始createBean
  • 上述的getSingletonBean进去,里面获取singletonObject = singletonFactory.getObject();【注释A】
try {
	singletonObject = singletonFactory.getObject();
	newSingleton = true;
}

singletonFactory是一个lambada表达式,因此执行其中的createBean方法获取结果。
createBean中又会调用doCreateBean()。
doCreateBean()就是一个创建Bean的过程。

4.doCreateBean中进行实例化并将Bean加入三级缓存
  • doCreateBean中首先实例化Bean,见createBeanInstance。
  • 实例化完成后会把实例化对象BeanA加入三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  • 注意addSingletonFactory里面没有获取lambada表达式的返回值,所以不会执行getEarlyBeanReference,只是将其设置为三级缓存的value
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				//value是lambada表达式传过来的一个工厂
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

由此可见,三级缓存保存的就是这个工厂,以lambada形式的表达式传过来

  • 所以当需要从三级缓存中获取对象时,从工厂中获取,如果需要AOP代理,那么其中也可完成。
    代理是在getEarlyBeanReference的exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);中完成的。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			//Bean生命周期中的实例化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//加入三级缓存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		Object exposedObject = bean;
		try {
			//Bean生命周期中的属性赋值
			populateBean(beanName, mbd, instanceWrapper);
			//Bean生命周期中的初始化
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}

		return exposedObject;
	}

5.属性注入与循环依赖产生
  • 加入三级缓存后BeanA继续进行(见上述代码中的属性注入和初始化)
    (1)属性赋值populateBean(beanName, mbd, instanceWrapper);,
    (2)初始化initializeBean(beanName, exposedObject, mbd);
  • 循环依赖就在属性赋值populateBean中产生,属性赋值中有可能要Autowired其他的BeanB
//截取populateBean中的部分代码
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
			//可以继续点击进去查看,里面会调用getBean()方法,而getBean其实又调用了doGetBean,看,是不是回到了开头。
			autowireByName(beanName, mbd, bw, newPvs);
		}
		// Add property values based on autowire by type if applicable.
		if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			autowireByType(beanName, mbd, bw, newPvs);
		}
  • 此时Autowired其他的BeanB,doGetBean中调用getSingleton(name,boolean),
  • 若BeanB还未创建
    • 那么会按上述流程继续创建BeanB;
    • 创建BeanB执行到属性注入阶段BeanB中要依赖注入BeanA;此时BeanA已经在三级缓存中,因此BeanB可以从三级缓存中获取到BeanA,获取到后将BeanA加入二级缓存并移出三级缓存。
    • BeanB执行完实例化,属性注入,初始化返回真正的BeanB,从而BeanA中可以注入BeanB.。
  • 若BeanB已经存在三级缓存当中
    • 那么getSingleton中会将其加入二级缓存,并从三级缓存中移除;
      这样子其实BeanB就成功注入BeanA了
@Nullable
	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) {
						//【注释B】通过getEarlyBeanReference获取Bean,如果需要代理则返回代理对象
						singletonObject = singletonFactory.getObject();
						//加入二级缓存
						this.earlySingletonObjects.put(beanName, singletonObject);
						//移出三级缓存
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
6.初始化完成Bean,加入一级缓存
  • 成功Autowired注入属性BeanB,属性注入完成,初始化完成,doCreateBean返回真正的BeanA实例;(doCreateBean–返回给–>createBean–返回给–>getSingleton)
  • getSingleton里面的singletonFactory.getObject()获取到BeanA,最后会把BeanA加入一级缓存
if (newSingleton) {
	addSingleton(beanName, singletonObject);
}

7.方法调用整理
  • getBean()->doGetBean()->getSingleton()->createBean()->doCreateBean()[这其中进行实例化(加入三级缓存),属性注入,初始化,获取到真正的Bean]
  • 其实这其中也是Bean生命周期的一个大概流程:
    实例化createBeanInstance()–>属性注入populateBean()–>初始化initializeBean()
    当然,Bean的销毁不在这其中
备注
注意上述有两个注释,【注释A】和【注释B】,这两个地方虽然都是singletonFactory.getObject(),但是singletonFactory是不一样的lambada表达式;
【注释A】是:
		() -> {
			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;
			}
		}
【注释B】是:
		() -> getEarlyBeanReference(beanName, mbd, bean)

二、总结

1.三级缓存的作用?

  • 三级缓存中保存的是工厂,如果Bean进行了AOP代理,可以通过工厂获取到一个代理对象,而不是对象本身。
  • 在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,
    如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

2.有了三级缓存工厂为什么还要二级缓存(二级缓存的作用)?
假设A中依赖B,B中依赖A,C中依赖A。A实例化后放入三级缓存,对于一个Bean不能多次从三级缓存拿:

  • 因为三级缓存保存的是ObjectFactory对象,一个对象工厂,通过工厂获取的对象可能是不一样的(上文分析过三级缓存保存的是工厂,以一个lambada表达式传过来);
  • 因此使用了二级缓存,B从三级缓存中获取A后就把A放入二级缓存,后面C就可以从二级缓存拿A了,这样能保证B和C拿到的对象A是一致的。
  • 注意:此时的A加入二级缓存还只是半成品,因为之前三级缓存中获取到A,说明还没完成属性注入和初始化(如果已经初始化完成会从三级缓存和二级缓存中删除,加入一级缓存),还不是最终初始化完成的Bean.
  • 当A完成属性注入,初始化完成后再放入一级缓存。

3.既然为了多个地方getBean()获取到同一个对象所以使用二级缓存,那为什么不直接在三级缓存中放入代理对象?

  • 如果这样那么意味着不管有没有循环依赖所有的Bean在实例化后都要完成AOP代理,违背了Spring在结合AOP跟Bean的生命周期的设计。
  • Spring的设计是在AbstractAutoProxyCreator.postProcessAfterInitialization完成AOP代理。
    (可以看到该类中wrapIfNecessary这个方法有2个调用,一个是为了解决循环依赖的getEarlyBeanReference,一个就是Spring计划完成代理的postProcessAfterInitialization)
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		//代理的实现
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

4.构造注入不能解决循环依赖

  • 因为构造方法创建实例,每次都要new一个要构造的实例bean,
    而A创建时,依赖B,就去创建B,B又依赖了A,继续构造A,如此循环下去 A(B) B(A) A(B)->…

5.原型(Prototype)多例场景是不支持循环依赖

  • 因为“prototype”作用域的Bean,为每一个bean请求提供一个实例,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean,会抛出异常。
    https://blog.csdn.net/W_317/article/details/108687587

6.三级缓存

  • 一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、注入、初始化完成的bean实例
  • 二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的bean实例
  • 三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。

参考文章:
https://mp.weixin.qq.com/s/fX4ze5YuLI5cqVaZhxq1tw
https://www.icode9.com/content-4-852133.html
https://javazhiyin.blog.csdn.net/article/details/107602783

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值