肝!!!手撕Spring循环依赖

别的先不扯,先上结论

Spring通过多个缓存解决了循环依赖,其中单例池缓存(singletonObjects),早期曝光对象(earlySingletonObjects)缓存,早期曝光对象工厂(singletonFactories)缓存。【之前大多数都定义为三级缓存,但是从某种意义上讲,这三个缓存之间貌似并没有层级关系,所以这里改为多个缓存Map对象】

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到早期曝光对象工厂(singletonFactories)缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取:

第一步,先获取到singletonFactories缓存中的工厂;

第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。

当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

接下来通过代码验证结论:

测试xml:

  <bean id="a" class="com.ziyang.cycle.A">
        <property name="b" ref="b"></property>
  </bean>
  <bean id="b" class="com.ziyang.cycle.B">
        <property name="a" ref="a"></property>
  </bean>

这里先定义两个bean A,B,分别注入彼此。

测试启动类:

 public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("cycle.xml");
    }

创建bean:

getBean()方法入口—>doGetBean()—>createBean()—>doCreateBean()

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 判断当前bean是否需要提前曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));

bean是否需要提前曝光判断依据:

  1. 是否是单例bean;
  2. 是否允许框架帮助我们解决循环依赖;
  3. 判断当前bean是否正在被创建;

第三点依据为:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation

将当前bean加入到singletonsCurrentlyInCreation集合中,这个集合中存放的就是正在创建过程中的beanName集合。

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory

​这里会将ObjectFactory保存到singletonFactories集合【早期曝光对象工厂(singletonFactories)缓存】中,可以通过getObject调用。也就是将beanA的ObjectFactory暴露到singletonFactories中。

按照正常结论,接下来在给beanA注入属性时,会获取BeanB。

// 对bean的属性进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean
populateBean(beanName, mbd, instanceWrapper);

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

在这里插入图片描述
pvs就是beanA的所有属性,这里看到,beanA的属性里有beanB。接下来就轮到在beanA中注入B了。主要在org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessaryorg.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference处理的。

在这里插入图片描述
这里会从beanfactory里去获取beanB,也就是循环调用了getBean方法。这里在创建beanB的时候,其实也是走了一遍getBean()—>doGetBean()—>createBean()—>doCreateBean()的一个流程,也会将banB放入早期曝光对象工厂(singletonFactories)缓存中
在这里插入图片描述

这时候会跟创建A一样,继续为B注入属性。
在这里插入图片描述
B的属性中又存在A,所以这时候会去创建A。
在这里插入图片描述
这时候,在创建B的时候注入属性A时,去创建A了,看下早期曝光对象工厂(singletonFactories)缓存中是有提前暴露的A。
在这里插入图片描述
在B中注入属性A的时候,会先去缓存singletonObjects去找,找到就返回,但是此时的A还没初始化,还未放入到单例池缓存(singletonObjects)中,属性B就来搞事情了。
所以单例池缓存(singletonObjects)里没有A,并且判断isSingletonCurrentlyInCreationA正在创建中,就会依次从earlySingletonObjects早期曝光对象(earlySingletonObjects)缓存中获取。如果早期曝光对象(earlySingletonObjects)缓存中没有,就会锁住单例池缓存(singletonObjects),依次从单例池缓存(singletonObjects),早期曝光对象(earlySingletonObjects)缓存,早期曝光对象工厂(singletonFactories)缓存中获取,显而易见,这里会在早期曝光对象工厂(singletonFactories)缓存中获取到。
在这里插入图片描述
这里会从早期曝光对象工厂(singletonFactories)缓存中获取,objectFactory,调用getObject方法,返回一个未初始化的A,并放入早期曝光对象(earlySingletonObjects)缓存中。

这时候B的属性A其实是一个未初始化的对象,接下来B会走完创建流程。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

Q:为什么不直接使用早期曝光对象(earlySingletonObjects)缓存来解决循环依赖,而使用早期曝光对象工厂(singletonFactories)缓存?

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
/**
	 * 此方法是处理器处理的,一般的处理器不做任何处理,只有AOP的处理器会去处理
	 *
	 * Obtain a reference for early access to the specified bean,
	 * typically for the purpose of resolving a circular reference.
	 * @param beanName the name of the bean (for error handling purposes)
	 * @param mbd the merged bean definition for the bean
	 * @param bean the raw bean instance
	 * @return the object to expose as bean reference
	 */
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

如果要使用earlySingletonObjects缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。
当aop存在时,从singletonFactories缓存出来的实例是代理。即aop存在时,需要将依赖对象从原始实例变为代理实例了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值