
系列博文:
【老王读Spring IoC-0】Spring IoC 引入
【老王读Spring IoC-1】IoC 之控制反转引入
【老王读Spring IoC-2】IoC 之 BeanDefinition 扫描注册
【老王读Spring IoC-3】Spring bean 的创建过程
【老王读Spring IoC-4】IoC 之依赖注入原理
【老王读Spring IoC-5】Spring IoC 小结——控制反转、依赖注入
相关阅读:
【Spring源码三千问】@Resource 与 @Autowired 的区别
【Spring源码三千问】bean name 的生成规则
【Spring源码三千问】BeanDefinition详细分析
【Spring源码三千问】Spring 是怎样解决循环依赖问题的?
大家都知道 Spring 解决了循环依赖的问题,网上也可以搜到 Spring 是使用三级缓存来解决循环依赖的。
那大家思考过没有,Spring 为什么要用三级缓存来解决循环依赖问题?使用两级缓存行不行?只使用一级缓存行不行?
哪些循环依赖场景是 Spring 没办法解决的?
本文先分析一下, Spring 是如何通过三级缓存来解决循环依赖问题的。
后续再来分析一下,为什么要用三级缓存,以及哪些循环依赖场景是 Spring 也解决不了的。
Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)
正文前面我们在分析bean 的创建过程时讲到,bean 的创建是由 AbstractBeanFactory#getBean() 触发的。
跟一下 AbstractBeanFactory#getBean(java.lang.String) 的源码,会发现它会调用 DefaultSingletonBeanRegistry#getSingleton()
// DefaultSingletonBeanRegistry#getSingleton()
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
// 一级缓存中没有 beanName, 且 beanName 正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
// bean 的早期引用中没有 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) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
以上就是三级缓存的代码。
那我们就来好好研究一下,它是如何来解决循环依赖的!
三级缓存的定义如下:
// DefaultSingletonBeanRegistry.java // 用于存放已经完全初始化好的 bean private final Map准备例子singletonObjects = new ConcurrentHashMap<>(256); // 用于存放 bean 的早期引用(都会存放,循环依赖时才会使用) private final Map > singletonFactories = new HashMap<>(16); // 用于存放三级缓存 ObjectFactory 获取到的对象(循环依赖时才会存放并使用) private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);
我们准备一个最简单的循环依赖的例子,A --> A 的场景:
@Service
public class AService {
@Autowired
AService aService;
public AService getaService() {
return aService;
}
}
public class CircleTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.kvn.beans.circle");
AService a = applicationContext.getBean(AService.class);
System.out.println(a + "--" + a.getaService());
}
}
程序可以正常运行,且 AService 中注入的 AService 是它自身。
这也就证明了 Spring 是解决了循环依赖的问题的。
AbstractBeanFactory#getBean() 在触发 bean 的加载时,会先从缓存中获取 bean,也就是会调用 DefaultSingletonBeanRegistry#getSingleton() 方法。
通过上面最简的例子调试 DefaultSingletonBeanRegistry#getSingleton(),会发现:
AService 初始化过程中,第一次调用 getSingleton() 时,一级缓存 singletonObjects 中没有 AService,且 bean 也没有在创建中。
后面在 createBean() 创建 AService 的实例时,会将 bean 的早期引用通过 ObjectFactory 放到三级缓存 earlySingletonObjects 中进行暴露。
在 populateBean() 注入依赖时,会第二次调用 getSingleton() ,此时 AService 已经在创建中,且三级缓存 earlySingletonObjects 中已经有 bean 的早期引用了。
这时,就可以注入进去了。
事实上,第一次调用 getSingleton() 时,三个缓存中都没有 AService
三级缓存 earlySingletonObjects 中暴露的 bean 的早期引用是一个 ObjectFactory 对象。
第二次调用 getSingleton() 时的断点图:
bean 在创建时,缓存的使用情况:
bean 的创建过程分三个阶段:
1 创建实例 createBeanInstance
2 填充依赖 populateBean
3 initializeBean
下图是 bean 的创建流程:
在没有循环依赖的情况下:
新创建一个 bean 实例后,会先将 bean 的早期引用通过 ObjectFactory 暴露到三级缓存 singletonFactories 中。
bean 初始化完成后,会清除掉相应的二级缓存、三级缓存,同时,将初始化完成的 bean 放入一级缓存 singletonObjects 中。
在有循环依赖的情况下:(A --> B --> A 的场景)
Spring 通过 三级缓存 为我们解决了循环依赖的问题。
三级缓存的定义和使用情况如下:
private final MapsingletonObjects = new ConcurrentHashMap<>(256); // 非循环依赖的场景,不会存放二级缓存 // 在循环依赖的场景下,才会通过三级缓存的 ObjectFactory 来获取对象,放到二级缓存中使用 private final Map earlySingletonObjects = new ConcurrentHashMap<>(16); // 不管是否有循环依赖,在 createBeanInstance 之后都会存放三级缓存 // 循环依赖时,才会使用到三级缓存,通过三级缓存来获取对象,放到二级缓存中,同时删除掉三级缓存 private final Map > singletonFactories = new HashMap<>(16);
思考:
既然 Spring 为我们解决了循环依赖的问题,但是我们有时还是会碰到循环依赖的报错:Is there an unresolvable circular reference?
那么,什么情况下 Spring 解决不了循环依赖的问题呢?
了解更多,请戳: 【Spring源码三千问】哪些循环依赖问题Spring解决不了?
如果本文对你有所帮助,欢迎点赞收藏!