您当前的位置:首页业界 >  >> 
别再让循环依赖成为你的噩梦,Spring助你一臂之力
来源:哔哩哔哩      时间:2023-05-01 13:12:12

大家好,我是小米,今天我们来聊聊 Spring 框架中的一个重要话题:循环依赖。在开发中,我们可能会遇到这样的问题:两个或多个 bean 之间相互依赖,导致创建对象时出现死循环。那么,Spring 是如何解决这个问题的呢?


【资料图】

什么是循环依赖

在 Spring 中,bean 之间的依赖关系可以通过构造函数注入、Setter 注入、接口注入等方式实现。当两个或多个 bean 相互依赖时,就会形成循环依赖。比如,bean A 依赖于 bean B,而 bean B又依赖于 bean A,这就是循环依赖。

Spring 如何解决循环依赖

Spring使用“三级缓存”来解决循环依赖的问题。三级缓存指的是singletonObjects、earlySingletonObjectssingletonFactories三个Map。

在Spring创建对象时,会先检查 singletonObjects 中是否已经存在该对象的实例,如果存在,则直接返回该实例;如果不存在,则检查 earlySingletonObjects 中是否已经存在该对象的“提前曝光”的代理对象,如果存在,则返回该代理对象,否则就调用singletonFactories 中存储的工厂方法来创建该对象的实例,并将其放入 earlySingletonObjects 中,同时存储一个 Factory 对象到singletonFactories 中。

当对象创建完成之后,就会从 earlySingletonObjects中 移除该对象的代理对象,将完整的对象实例放入 singletonObjects 中,并清空 singletonFactories中的Factory 对象。这样,下次获取该对象的实例时,就可以直接从 singletonObjects 中获取了。

三级缓存是如何解决循环依赖

当出现循环依赖时,Spring 会将正在创建的对象提前曝光,也就是将一个代理对象放到 earlySingletonObjects 中,然后在创建对象时,将代理对象注入到另一个需要依赖该对象的 bean 中。这样,当需要使用该对象时,就可以从 earlySingletonObjects 中获取到代理对象,避免了死循环的出现。

缓存为什么是三级

三级缓存可以保证对象的单例性,同时也可以解决循环依赖的问题。而单例对象的创建和获取是很频繁的操作,所以使用三级缓存可以提高效率。

缓存的放置时间和删除时间

三级缓存:当 Spring 创建一个bean的实例时,会将其放入singletonFactories中,这个操作是在createBeanInstance之后完成的。同时,也会将该 bean 的工厂方法放入singletonFactories中。这样,当需要获取该 bean 的实例时,就可以从 singletonFactories 中获取到其对应的工厂方法。

二级缓存:当 Spring 从 singletonFactories 中获取到一个bean 的工厂方法后,会通过该工厂方法创建该 bean 的实例,并将其放入 earlySingletonObjects 中。同时,也会将其代理对象放入 earlySingletonObjects 中。

在后续创建依赖该 bean 的其他 bean 时,如果需要获取该bean 的实例,就会从 earlySingletonObjects 中获取到其代理对象。在第一次从 earlySingletonObjects 中获取到该代理对象时,Spring 会判断该对象是代理对象还是普通对象,如果是代理对象,则会将其替换为其对应的普通对象,并从 earlySingletonObjects 中删除该代理对象。

一级缓存:当Spring创建完一个bean的完整实例后,会将其放入 singletonObjects 中,并从 earlySingletonObjects中删除其对应的代理对象和工厂方法。同时,也会将其对应的bean的依赖项从 dependencyCheck 中移除。这样,下次获取该bean的实例时,就可以直接从 singletonObjects 中获取了。

提前曝光来解决循环依赖(不推荐)

除了三级缓存之外,提前曝光是 Spring 解决循环依赖问题的重要手段之一。当 Spring 创建一个 bean 的实例时,如果检测到其依赖了另一个正在创建的 bean,则会将其提前曝光,即将一个代理对象放入 earlySingletonObjects 中,以便在后续创建依赖该 bean 的其他 bean 时,可以直接使用其代理对象,避免了死循环的出现。不过,提前曝光的方法需要手动配置,比较麻烦,所以一般情况下,我们都会使用 Spring 提供的三级缓存来解决循环依赖的问题。

END

总的来说,Spring 是一个非常优秀的 Java 框架,它不仅提供了依赖注入和 AOP 等常用功能,还能够很好地解决循环依赖的问题。而这些都离不开 Spring 框架底层的设计和实现。希望今天的分享能够帮助大家更好地理解 Spring 框架的原理和实现,也希望大家能够继续深入学习和研究 Java 技术,不断提升自己的能力。谢谢大家的阅读!

如有疑问或者更多的技术分享,欢迎关注我的微信公众号“知其然亦知其所以然”!

标签:

X 关闭

X 关闭