SpringBean循环依赖及解决办法

温馨提示:在学习循环依赖之前,先学习SpringBean的生命周期

一、循环依赖是什么?

Spring有个很重要的特性,在容器创建某个对象的时候,发现这个对象中的属性需要注入另一个对象,此刻会先去创建另一个对象。

根据上述特性,对象之间如果存在“你中有我,我中有你",对象间互相依赖的情况,就构成了循环依赖。

如下代码所示(你心中有我,我心中有你):
HeartA:

@Data
@Component
public class HeartA {

    @Autowired
    HeartB heartB;
}

HeartB:

@Data
@Component
public class HeartB {

    @Autowired
    HeartA heartA;
}

二、解决方法

1. 提前暴露法。

在初始化 HeartA 的属性之前,也就是注入 HeartB对象之前,先把 HeartA实例化后的对象放到一个缓存中,提前暴露实例化后的半成品的HeartA,这样在创建 HeartB注入 HeartA对象时,先从这个缓存中获取 HeartA实例化后的对象,给HeartB注入一个半成品的HeartA,这样就解决了循环创建的问题。如下图所示:

在这里插入图片描述
2. 懒加载@Lazy

将@Lazy 放在@Autowired注解的属性上,在注入的时候并不会直接给属性赋上真正的值,只会注入一个代理对象,当真正使用到这个属性的时候,才回去容器中找到一个符合的对象。

三、三级缓存

getSingleton:

在这里插入图片描述

  • 一级(singletonObjects):存放成品对象
  • 二级(earlySingletonObjects):存放半成品对象
  • 三级(singletonFactories):用于获取提前暴露的半成品对象

singletonFactory.getObject():

获取提前暴露的bean,对于特殊的bean,在getObject中有特殊的处理。

getObject()核心代码是getEarlyBeanReference,在这个方法中调用所有的SmartInstantiationAwareBeanPostProcessor的实现类,对bean做特殊处理,如果是个普通bean,每次getObject()返回的都是同一个bean,如果是需要特殊处理的bean,比如需要Aop织入,则每次都会特殊处理一次,每次获取的都是不同的bean。

	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;
	}

第二节的流程图是通过二层缓存(singletonObjects、singletonFactories)解决循环依赖的,但为什么要用到三层缓存(earlySingletonObjects)呢?

原因:在创建的过程中,实例化后的bean,并不会传入到每个方法中,都是用getSingleton(beanName)的方式获取当前状态的bean。创建的对象只是一个普通对象的话,多次getSingleton()返回的是同一个bean。如果创建的是需要特殊处理的bean,多次getSingleton()返回的都是不同的bean,设置earlySingletonObjects缓存,在第一次特殊处理bean之后,就将特殊处理的bean存入earlySingletonObjects缓存,这样每次getSingleton()获取bean的时候,会先尝试从earlySingletonObjects缓存中获取,不再创建特殊bean了。

举个例子:第一次调用getSingleton(“heartA”),获取到了代理对象A1,随后对这个A1对象做了一些操作,之后又调用了一次getSingleton(“heartA”),获取到了代理对象A2,这个时候就有问题了,创建heartA的过程中,同时出现了两个不同的对象。

四、源码分析

1. doCreateBean中提取暴露bean

实例化后,判断有循环依赖,将实例化后的bean存入singletonFactories中,提取暴露bean。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

        ....代码省略
         
	    // 实例化bean
		instanceWrapper = createBeanInstance(beanName, mbd, args);

        ....代码省略

	    // 可以修改BeanDefinition的扩展点
		applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
	

		// 此处是如果发生了循环依赖的情况,提前暴露没创建完的bean,可以称为半成品的bean或实例化bean
		// getEarlyBeanReference方法中执行SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法
		// getEarlyBeanReference中可以对bean做一些操作,主要应用是Aop织入
		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");
			}
			// 提前暴露bean的获取,存入singletonFactories中,调用singletonFactory.getObject()获取bean
			// singletonFactory.getObject() = () -> getEarlyBeanReference(beanName, mbd, bean)
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

	    // 属性赋值
		populateBean(beanName, mbd, instanceWrapper);
		// 初始化bean
		exposedObject = initializeBean(beanName, exposedObject, mbd);

        ....代码省略
		

	    // 这里判断在初始化的过程中,bean是否还是原来的bean,如果不是就会在下面的分支中报错
        ....代码省略
        
		// 注册到 disposableBeans 里面.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

2. getSingleton获取提前暴露的半成品对象:

在这里插入图片描述
3. getObject中又调用的getEarlyBeanReference

获取所有的SmartInstantiationAwareBeanPostProcessor对bean特殊处理,没有特殊处理,直接返回传入的bean(exposedObject)。

在这里插入图片描述
4. SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference():

下面代码是实现类AbstractAutoProxyCreator的getEarlyBeanReference()

在这里插入图片描述
5. wrapIfNecessary

在这个方法中,判断这个bean需不需要创建代理对象,需要则创建代理对象,反之返回传进来的原bean。
在这里插入图片描述
6. 把第一次获取到的半成品对象,放入earlySingletonObjects缓存,避免多次重复创建代理对象。

在这里插入图片描述

五、总结

  1. 循环依赖的构成,是因为多个对象间的互相依赖造成的。
  2. 解决办法:提前暴露实例化后的bean或者懒加载。
  3. 用三级缓存的原因是,防止多次getSingleton导致多次创建代理对象。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吖土豆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值