Spring中的循环依赖问题

目录

1、什么是Spring的循环依赖?

2、如何避免循环依赖问题?

3、Spring的三级缓存

小结


1、什么是Spring的循环依赖?

Spring框架中的循环依赖问题是指两个或多个bean之间相互依赖,形成闭环,导致无法完成实例化的问题。简单来说,就是A依赖于B,而B又依赖于A,形成了一个循环的依赖链。

循环依赖问题可能导致应用程序启动失败或者产生不可预期的行为。这是因为当Spring容器创建Bean时,会使用默认的构造函数或Setter方法将依赖注入到Bean中。如果存在循环依赖,那么容器无法确定先创建哪个Bean,从而导致无法完成依赖注入。

⭐扩展:Bean的创建过程:

图片来源:深谈Spring如何解决Bean的循环依赖

在Spring中,循环依赖主要分为两种类型:构造器的循环依赖和field属性的循环依赖。

2、如何避免循环依赖问题?

首先需要明确的一点是,Spring 并不能解决所有循环依赖的问题。Spring提供了以下几种解决循环依赖问题的方式:

  1. 构造器注入:Spring容器在创建bean时,会先创建所有没有依赖关系的bean,然后再创建有依赖关系的bean。在创建有依赖关系的bean时,Spring会先创建构造器参数中所需要的bean,然后再创建当前bean。使用构造函数注入代替Setter方法注入,可以确保在创建Bean时所有的依赖都已经提供,这种方式可以解决大部分的循环依赖问题。

  2. 使用setter方法注入依赖:在这种情况下,Spring容器可以在实例化bean后通过调用setter方法来注入所需的依赖。

  3. 使用@Lazy注解:@Lazy注解可以延迟加载bean的实例化。通过将Bean设置为延迟加载,当需要使用该bean时才会进行实例化。这种方式可以解决部分的循环依赖问题。

  4. 使用@Autowired注解搭配@Qualifier注解明确指定依赖关系。通过使用@Qualifier注解,可以明确指定依赖的Bean名称,从而帮助Spring容器正确解析循环依赖。

  5. 使用@PostConstruct注解和InitializingBean接口。这两种方式可以在Bean创建完成后执行特定的初始化操作,可以在初始化方法中处理循环依赖的情况。

  6. 使用代理:在这种情况下,可以使用AOP代理来实现bean之间的依赖关系。这样,就可以在编译时就解决循环依赖问题。

图片来源:https://www.cnblogs.com/mghio/p/15024461.html

3、Spring的三级缓存

在Spring中,Bean的创建过程中涉及到三级缓存(三级缓存是在Spring 4.x之前的版本中使用的机制):

  1. singletonObjects:这是一级缓存,用于存储完全初始化并准备好的单例Bean实例。这些Bean实例是最终被返回的单例Bean实例。在缓存中,Bean的名字和Bean实例是以键值对的形式存在的。当Bean的依赖注入完成并且初始化后,它会被放置在这个缓存中。

  2. earlySingletonObjects:这是二级缓存,用于存放已经创建,但还未完成初始化的单例Bean实例。这些Bean实例通常是因为依赖其他Bean实例而无法完成初始化,处于不完整状态。在Bean的初始化过程中,如果发现循环依赖,则会将尚未完全初始化的Bean放置在这个缓存中,以便解决循环依赖问题。

  3. singletonFactories:这是三级缓存,用于存储用于创建单例Bean的ObjectFactory工厂对象,这些工厂对象可以用来创建单例Bean实例。当Spring正在创建一个Bean时,如果发现了循环依赖,则会将该Bean的创建工厂放置在这个缓存中,在需要时可以通过该工厂来获取Bean的实例。

图片来源:Spring 的循环依赖问题 - mghio - 博客园

当两个相互依赖的Bean需要被实例化时,Spring会先查看第一级缓存中是否已经有完整的Bean实例。如果有,就使用已有的实例;如果没有,则进入第二级缓存查看是否有已经创建但未初始化的Bean实例。如果有,就使用这个未初始化的Bean实例去初始化另一个Bean,然后再将这个未初始化的Bean实例存入第一级缓存;如果没有,则进入第三级缓存查看是否有可以用来创建Bean实例的工厂对象。如果有,就使用这个工厂对象去创建Bean实例,然后再将这个新创建的Bean实例存入第一级缓存;如果没有,则直接创建新的Bean实例存入第一级缓存。

通过三级缓存机制,Spring可以在循环依赖的情况下正确地初始化每个Bean,避免了出现错误或异常。同时,三级缓存也有效地减少了不必要的重复初始化操作,提高了应用程序的性能。

这三级缓存的使用可以帮助Spring容器在处理循环依赖时能够正确地获取到Bean的实例,并最终完成整个Bean的创建和初始化过程。在Spring 5.x及更新的版本中,已经不再使用三级缓存,而是采用了更加高效和可靠的解决方案来处理循环依赖的问题。

小结

需要注意的是,尽管Spring提供了一些机制来解决循环依赖问题,但是最好的做法仍然是尽量减少组件之间的相互依赖,尽量保持低耦合的设计,从而避免出现循环依赖的情况。良好的设计和架构能够减少循环依赖的发生,提高应用程序的可维护性和可测试性。

参考:

Spring 的循环依赖问题 - mghio - 博客园

深谈Spring如何解决Bean的循环依赖

今天一定要搞清楚Spring如何解决循环依赖

Spring 循环依赖解决方案_spring解决循环依赖-CSDN博客


相关推荐
考虑考虑1 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261351 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊3 小时前
Java学习第22天 - 云原生与容器化
java
渣哥4 小时前
原来 Java 里线程安全集合有这么多种
java
间彧4 小时前
Spring Boot集成Spring Security完整指南
java
间彧5 小时前
Spring Secutiy基本原理及工作流程
java
Java水解6 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆8 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学8 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole8 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端