Spring源码:Spring如何解决循环依赖问题

前言

Spring如何解决循环依赖问题是一个面试中经常问到的面试题。笔者前段时间去面试的时候,也遇到了这么一个题目。但是,大部分的人应该是也是“八股文”选手,临阵磨枪背了那几个Map的名字(包括笔者自己~)。没有深入到源码级别一探究竟。因此,经过学习,记录一下。

1.什么是循环依赖

很简单。比如下面的代码

@Component
public class OrderService {
	@Autowired
	 UserService userService;

	public OrderService() {
		System.out.println("333");
	}

}

@Component
public class UserService {

	@Autowired
	OrderService orderService;

	public UserService() {
		System.out.println("init userService");
	}
}

就是这么简单。

2.Spring支持循环依赖吗?

那是理所当然的,而且Spring默认是支持循环依赖的。(ps:需要注意的是只有当我们使用setter注入和注入的依赖是单例的时候,才会生效。如果我们使用构造方法的方式注入,会报错)
原因:我们可以在AbstractAutowireCapableBeanFactory这个的类中的allowCircularReferences属性就知道,默认是true

AbstractAutowireCapableBeanFactory {
	/** Whether to automatically try to resolve circular references between beans. */
	private boolean allowCircularReferences = true;
	}

而且我们也可以通过Api的方式,来关闭循环依赖的校验
修改Spring循环依赖
具体的效果,大家可以自己写个DEMO去校验一下~

3.Spring具体是怎么解决

我们知道Spring在管理Bean的时候,需要经历初始化和实例化。当中会有一段的生命周期,其中在实例化的阶段,就会处理Spring的循环依赖。
下面是具体的流程。
假设我们前面已经完成了很多前置条件,那么要处理的就是属性注入问题了。

3.1 属性注入

在这里插入图片描述

3.2 设置属性

在这里插入图片描述
这里是通过Spring的后置处理器,解决注入问题,因为我们是@AutoWired的。因此,对应的后置处理器是AutoWiredAnnotationBeanPostProcessor
然后,执行流程如下

  1. PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
  2. 执行InjectionMetadatainject - > element.inject(target, beanName, pvs);【注意:这里调用的是AutowiredFieldElement的inject,第3,、4的代码都是在这个类中】
  3. 然后执行value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);去找对应的依赖
  4. 最后,将找到的值,set进file中。
    在这里插入图片描述
3.3 寻找UserService

从上面看出我们知道,最终Spring对某个file设置值的流程。那么我们是怎么找到对应的UserService的呢?关键在于上面的步骤中的value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)。下面就是Spring真正解决循环依赖的流程。

经过上面的流程我们可以看到又回到了populateBean这个方法。但是BeanName换成了
在这里插入图片描述
在这里插入图片描述
这个时候UserService发现又需要OrderService,又走了一遍getBean这个方法的流程。
在这里插入图片描述

经过上面的流程又跑到了创建Bean的流程。。。
在这里插入图片描述
在getSingleton方法内部,我们可以到orderService是为null的。但是由于
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))
这段代码的存在,因此可以进入if方法的内部:里面的步骤如下:

  1. 首先判断一下earlySingletonObjects这个Map中有没有数据;
  2. 如果没有的话则从singletonFactories这个Map中拿到我们之前doCreateBean方法中放进去的ObjectFactory。然后创建一个Object。
  3. 最后将创建的Object放进earlySingletonObjects中,然后移除singletonFactories中的数据。

在这里插入图片描述
画外音:

  • 我们看到isSingletonCurrentlyInCreation这么一个集合是用来判断是否判断是否在实例化的。
    而这集合是在beforeSingletonCreation这个方法放进去的。
  • singletonObjects:是在我们完成所有Bean的实例化后,放进去的单例池,他也是一个Map。

4.常见面试题

4.1 Spring为什么采用三级缓存的方式解决循环依赖?用二级缓存不可以吗?

这里先同步一个概念:

  • singletonObjects:一级缓存;
  • earlySingleObjects:二级缓存;
  • singletonFactories:三级缓存;

如果我们只用二级缓存。那么就只是到了earlySingleObjects这个缓存。如果我们是普通的Bean是没问题的,但是我们知道Spring还有AOP这个玩意。如果缺少了二级缓存,那么如果出现上面的循环依赖的话,有可能一方注入的普通的Bean,而不是增强后的Bean。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值