前言
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的方式,来关闭循环依赖的校验
具体的效果,大家可以自己写个DEMO去校验一下~
3.Spring具体是怎么解决
我们知道Spring在管理Bean的时候,需要经历初始化和实例化。当中会有一段的生命周期,其中在实例化的阶段,就会处理Spring的循环依赖。
下面是具体的流程。
假设我们前面已经完成了很多前置条件,那么要处理的就是属性注入问题了。
3.1 属性注入
3.2 设置属性
这里是通过Spring的后置处理器,解决注入问题,因为我们是@AutoWired
的。因此,对应的后置处理器是AutoWiredAnnotationBeanPostProcessor
。
然后,执行流程如下
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
- 执行
InjectionMetadata
的inject
- >element.inject(target, beanName, pvs);
【注意:这里调用的是AutowiredFieldElement的inject,第3,、4的代码都是在这个类中】 - 然后执行
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
去找对应的依赖 - 最后,将找到的值,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方法的内部:里面的步骤如下:
- 首先判断一下earlySingletonObjects这个Map中有没有数据;
- 如果没有的话则从singletonFactories这个Map中拿到我们之前
doCreateBean
方法中放进去的ObjectFactory。然后创建一个Object。 - 最后将创建的Object放进earlySingletonObjects中,然后移除singletonFactories中的数据。
画外音:
- 我们看到
isSingletonCurrentlyInCreation
这么一个集合是用来判断是否判断是否在实例化的。
而这集合是在beforeSingletonCreation
这个方法放进去的。 - singletonObjects:是在我们完成所有Bean的实例化后,放进去的单例池,他也是一个Map。
4.常见面试题
4.1 Spring为什么采用三级缓存的方式解决循环依赖?用二级缓存不可以吗?
这里先同步一个概念:
- singletonObjects:一级缓存;
- earlySingleObjects:二级缓存;
- singletonFactories:三级缓存;
如果我们只用二级缓存。那么就只是到了earlySingleObjects这个缓存。如果我们是普通的Bean是没问题的,但是我们知道Spring还有AOP这个玩意。如果缺少了二级缓存,那么如果出现上面的循环依赖的话,有可能一方注入的普通的Bean,而不是增强后的Bean。