别再让循环依赖成为你的噩梦,Spring助你一臂之力精选

  • 佚名
  • 2023/5/6 14:48:44
大家好,我是小米,今天我们来聊聊 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 技术,不断提升自己的能力。谢谢大家的阅读!

责任编辑:水告    来源:知其然亦知其所以然

相似话题