Spring-源码-三级缓存、循环依赖问题

一、下面看下普通Bean循环依赖,怎么解决

案例:A依赖B,B依赖A

1、A先实例化,同时把A放到三级缓存中,在初始化A对象里面的属性,发现有依赖B
2、创建实例B, 再把B放到三级缓存中,发现B又依赖A,执行属性填充也就是populateBean方法,里面会调用getSingleton方法
Object sharedInstance = getSingleton(beanName);

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 != NULL_OBJECT ? singletonObject : null);
}
3、依次从存缓冲去获取A,其中一级、二级缓存中都没有,在从三级缓存中取,发现有,从三级缓存中取出,注意类型是ObjectFactory,然后调用getObject()方法

getObject实际调用的是getEarlyBeanReference方法

getEarlyBeanReference里面通过,拿到BeanPostProcess判断是否是SmartInstantiationAwareBeanPostProcessor类型,如果是调用getEarlyBeanReference方法

最终会吧beanName加到earlyProxyReferences里面,调用wrapIfNecessary方法,该方法会判断这个bean是否是被切面处理,如果不是返回原始实例的Bean

为什么要添加到earlyProxyReferences里面去?因为对于AOP在执行AnnotationAwareAspectJAutoProxyCreator的后置处理器的时候,也会创建代理对象,这里就是为了防止重复创建,通过earlyProxyReferences缓存保存状态,如果创建过代理对象就存入earlyProxyReferences里面去。

4、调用完了后,已经拿到了AService的实例对象,但是此时的AService还没有初始化属性,是个半成品,在把AServcie存入二级缓存,同时清除三级缓存。(面试会问可不可以不要二级缓存,如果没有二级缓存这个AService因为没有完全实例化好,必须得放到二级缓存)
5、到这里BService实例已经创建好了,同时属性也赋好了值
6、把BService从三级缓存中移除,同时添加到一级缓存
7、至此BService已经到了一级缓存,而A已经从三级缓存转到了二级缓存(步骤4)
8、此时AService的属性初始化也完成了
9、在调用一次getSingleton方法,因为前面我们把A从三级缓存转到了二级缓存,所以调用getSingleton方法时allowEarlyReference传的是false,先从一级拿再从二级里面拿,为什么还要在调用一次getSingleton方法,原因是,如果Bean是AOP代理,那么二级存的就是已经生成好的代理,而这个生成好的代理对象,已经被BService地址引用了,所以必须返回引用对象,才能保证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);
				
				//allowEarlyReference传的是false,不在从三级缓存中拿
				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 != NULL_OBJECT ? singletonObject : null);
	}

问题:

上面有个问题:

//1、先实例化AService
exposedObject = initializeBean(beanName, exposedObject, mbd);

//2、然后又从二级缓存中拿到了AService,在返回
Object earlySingletonReference = getSingleton(beanName, false);

既然二级缓存中已经有了,那么步骤1是不是有点多余了,其实不是的,我们先看

1、如果循环依赖都是普通的Bean首先,普通实例Bean和initializeBean方法返回的exposedObject是一个对象,包括二级缓存中拿到的earlySingletonReference都是一个地址

2、如果是AOP代理的Bean,那么二级缓存中拿到的Bean,是通过BService调用了AService的getEarlyBeanReference方法生成了代理Bean,为什么这么说,因为一开始实例化A的时候,已经吧AService的ObjectFactory存到了三级缓存中,而BService在处理依赖属性的时候,就调用了ObjectFactory的getEarlyBeanReference方法,来得到代理对象,最后由BService把A的代理对象Bean,存到了二级缓存中,所以在调用initializeBean方法,会返回代理对象,但是要注意,initializeBean创建代理对象走的是postProcessAfterInitialization方法,此时earlyProxyReferences里面是包含了AService,就不会在调用wrapIfNecessary方法。这样返回的Bean还是普通实例化的Bean

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

我们在看下ObjectFactory的getObject方法

if (earlySingletonExposure) {
	addSingletonFactory(beanName, new ObjectFactory<Object>() {
		@Override
		public Object getObject() throws BeansException {
			return getEarlyBeanReference(beanName, mbd, bean);
		}
	});
}

@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	if (!this.earlyProxyReferences.contains(cacheKey)) {
		this.earlyProxyReferences.add(cacheKey);
	}
	return wrapIfNecessary(bean, beanName, cacheKey);
}

上面这2个方法都是通过wrapIfNecessary来创建代理对象,postProcessAfterInitialization创建代理对象是由AService调用initializeBean方法触发的,getEarlyBeanReference方法是BService在初始化内部属性时,调用AService的ObjectFactory.getObject()触发的,且BService顺序在initializeBean之前,所以先执行,同时把BeanName计入缓存earlyProxyReferences中,这个很重要,当initializeBean调用postProcessAfterInitialization创建代理Bean的时候,会先从缓存里面读,发现AService代理对象已经创建好了,就不会在创建了。所以无论如何AService也不会触发多次创建。(一定要理解这段话)

上图又有一个疑问:AService在实例化的时候,会调用populateBean完成属性填充,此时填充的是A的普通实例对象,B创建的AService实例对象是代理对象,最后返回的也是A的代理对象,我们后面调用都是用的代理对象,而A的代理对象也没有完成属性填充,populateBean填充属性填充的操作对象,是普通实例对象,是不是填充了寂寞?

解答:其实不是,在spring的AOP代理对象属性填充,填充的就是普通实例Bean,而且这个普通实例Bean我们是要用到的,创建AOP代理Bean时,是要传普通实例Bean

以JDK代理为例,执行目标方法时,还是通过我们上面创建的普通Bean,来调用的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

信仰_273993243

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

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

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

打赏作者

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

抵扣说明:

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

余额充值