Spring循环依赖

什么是循环依赖

循环依赖就是比如A 依赖B B又依赖A 或者A 依赖A
当然也可以是三个 Bean 甚至更多的 Bean 相互依赖,原理都是一样的

@Service
public class A {
    @Resource
    private B  b;
}
@Service
public class B {

    @Resource
    private A a;
}

循环依赖可能会产生的问题

例如 A 要依赖 B,发现 B 还没创建于是开始创建 B ,创建的过程发现 B 要依赖 A, 而 A 还没创建好呀,因为它要等 B 创建好

解决循环依赖必须满足的条件

依赖的 Bean 必须都是单例

为什么必须是单例的原因

如果两个Bean是原型模式的话 那么创建 A1 需要创建一个 B1。创建 B1 的时候要创建一个 A2。创建 A2 又要创建一个 B2。创建 B2 又要创建一个 A3…因为原型模式都需要创建新的对象,不能用以前的对象
如果是单例模式的话 创建 A 需要创建 B,而创建的 B 需要的是之前的个 A 一直只会有一个对象

解决方案

先创建 A,此时的 A 是不完整的(没有注入 B),用个 map 保存这个不完整的 A,再创建 B ,B 需要 A。
所以从那个 map 得到“不完整”的 A,此时的 B 就完整了,然后 A 就可以注入 B,然后 A 就完整了,B 也完整了,且它们是相互依赖的
在这里插入图片描述

依赖注入的方式,必须不全是构造器注入,且 beanName 字母序在前的不能是构造器注入

不能全是构造器构造的原因

在Spring中创建Bean的三步
实例化,createBeanInstance,就是 new 了个对象
属性注入,populateBean, 就是 set 一些属性值
初始化,initializeBean,执行一些 aware 接口中的方法,initMethod,AOP代理等

如果全是构造器注入,比如A(B b),那表明在 new 的时候,就需要得到 B,此时需要 new B

但是 B 也是要在构造的时候注入 A ,即B(A a),这时候 B 需要在一个 map 中找到不完整的 A ,发现找不到 因为A 还没有New 所以找到不完整的 A,因此如果全是构造器注入的话,那么 Spring 无法处理循环依赖

总结

如果循环依赖都是构造器注入,则失败
如果循环依赖不完全是构造器注入,则可能成功,可能失败,具体跟BeanName的字母序有关系。因为 Spring 容器是按照字母序创建 Bean 的,A 的创建永远排在 B 前面。

Spring 解决循环依赖的方式

源码

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//先从一级缓存拿 拿到直接返回
		Object singletonObject = this.singletonObjects.get(beanName);
		// 如果没拿到 在 看对应的 Bean 是否在创建中 如果不在直接返回null
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		    //如果在 从二级缓存拿 拿到直接返回
			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) {
						   //三级缓存拿
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							//拿到了
							if (singletonFactory != null) {
							//获取 getObject
								singletonObject = singletonFactory.getObject();
								//塞二级缓存
								this.earlySingletonObjects.put(beanName, singletonObject);
                                 //清三级缓存
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

流程

1.一级缓存,singletonObjects,存储所有已创建完毕的单例 Bean (完整的 Bean)
2.二级缓存,earlySingletonObjects,存储所有仅完成实例化,但还未进行属性注入和初始化的 Bean
3.三级缓存,singletonFactories,存储能建立这个 Bean 的一个工厂,通过工厂能获取这个 Bean,延迟化 Bean 的生成,工厂生成的 Bean 会塞入二级缓存

1.获取单例 Bean 的时候会通过 BeanName 先去 singletonObjects(一级缓存) 查找完整的 Bean,如果找到则直接返回 如果没找到
2.看对应的 Bean 是否在创建中,如果不在直接返回找不到,如果是,则会去 earlySingletonObjects (二级缓存)查找 Bean,如果找到则返回 如果没找到
3.去 singletonFactories (三级缓存)通过 BeanName 查找到对应的工厂,如果存着工厂则通过工厂创建 Bean ,并且放置到 earlySingletonObjects 中 然后根据BeanName 将singletonFactories(三级缓存)的Bean移除 如果三个缓存都没找到则返回 null

根据上面的步骤可以得出结论 如果Bean还没有创建查询一级缓存查不到并且不再创建中直接就会返回都不会查询二三级缓存

返回 null 之后,说明这个 Bean 还未创建,这个时候会标记这个 Bean 正在创建中,然后再调用 createBean 来创建 Bean,而实际创建是调用方法 doCreateBean

		if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
						//创建Bean
							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;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

在实例化 Bean 之后,会往 singletonFactories 塞入一个工厂,而调用这个工厂的 getObject 方法,就能得到这个 Bean

AbstractAutowireCapableBeanFactory.doCreateBean() {
......
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
......
}

塞到工厂中式为了提前暴漏

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
		  //当一级缓存中不存在这个Bean
			if (!this.singletonObjects.containsKey(beanName)) {
			    //塞三级缓存
				this.singletonFactories.put(beanName, singletonFactory);
				//移除 二级缓存
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

1.然后就开始执行属性注入,这个时候 A 发现需要注入 B,所以去 getBean(B),此时又会走一遍上面描述的逻辑,到了 B 的属性注入这一步
2.此时 B 调用 getBean(A),这时候一级缓存里面找不到,但是发现 A 正在创建中的,于是去二级缓存找,发现没找到,于是去三级缓存找,然后找到了
3.并且通过上面提前在三级缓存里暴露的工厂得到 A,然后将这个工厂从三级缓存里删除,并将 A 加入到二级缓存中。
然后结果就是 B 属性注入成功。
4.紧接着 B 调用 initializeBean 初始化,最终返回,此时 B 已经被加到了一级缓存里 。
5.这时候就回到了 A 的属性注入,此时注入了 B,接着执行初始化,最后 A 也会被加到一级缓存里,且从二级缓存中删除 A
6.重点就是在对象实例化之后,都会在三级缓存里加入一个工厂,提前对外暴露还未完整的 Bean,这样如果被循环依赖了,对方就可以利用这个工厂得到一个不完整的 Bean,破坏了循环的条件

为什么需要三级缓存 二级缓存不够么?

如果仅仅只是为了破解循环依赖,二级缓存够了
但是在调用工厂的getObject时候

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		//如果是 true 说明有 InstantiationAwareBeanPostProcessors
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		//且循环的是 smartInstantiationAware 类型,如有这个 BeanPostProcessor 说明 Bean 需要被 aop 代理
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}

如果有代理的话,那么我们想要直接拿到的是代理对象
1.也就是说如果 A 需要被代理,那么 B 依赖的 A 是已经被代理的 A,所以我们不能返回 A 给 B,而是返回代理的 A 给 B
2.这个工厂的作用就是判断这个对象是否需要代理,如果否则直接返回,如果是则返回代理对象
3.正常代理对象的生成是基于后置处理器,是在被代理的对象初始化后期调用生成的,所以如果你提早代理了其实是违背了 Bean 定义的生命周期
4.所以 Spring 先在一个三级缓存放置一个工厂,如果产生循环依赖,那么就调用这个工厂提早得到代理对象
5.如果没产生依赖,这个工厂根本不会被调用,所以 Bean 的生命周期就是对的
在这里插入图片描述

总结

1.有构造器注入,不一定会产生问题,具体得看是否都是构造器注和 BeanName 的字母序
2.如果单纯为了打破循环依赖,不需要三级缓存,两级就够了
3.三级缓存是否为延迟代理的创建,尽量不打破 Bean 的生命周期
4.加@Lazy 也可以解决

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖指的是在Spring中,多个Bean之间存在相互依赖的情况。具体来说,当一个Bean A依赖于另一个Bean B,同时Bean B也依赖于Bean A时,就形成了循环依赖。这种情况下,Spring需要解决Bean的创建和依赖注入的顺序问题。 在Spring中,循环依赖问题是由于Bean的生命周期所引起的。Spring的Bean生命周期包括了Bean的实例化、属性注入、初始化以及销毁等过程。当出现循环依赖时,Spring会通过使用“提前暴露”的方式来解决这个问题。 具体来说,当Spring创建Bean A时,发现它依赖于Bean B,于是会创建一个A的半成品对象,并将其暂时放入一个缓存中。然后,Spring会继续创建Bean B,并将其注入到A的属性中。接着,Spring会继续完成B的创建,并将其放入缓存中。最后,Spring会将A的半成品对象交给B进行依赖注入,完成A的创建,并将其从缓存中移除。 需要注意的是,Spring循环依赖有一定的限制条件。例如,如果Bean A和Bean B都是单例模式,那么它们之间的循环依赖是无法解决的。因为单例模式下,Bean的创建和依赖注入是同时进行的,无法通过缓存来解决循环依赖。在这种情况下,程序员需要手动调整Bean的依赖关系或使用其他解决方案来避免循环依赖的问题。 综上所述,Spring循环依赖是指在Spring中多个Bean之间存在相互依赖的情况。Spring通过使用缓存和提前暴露的方式来解决循环依赖问题,但在某些情况下有一定的限制条件需要注意。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值