Spring中的循环依赖问题

目录

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

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

3、Spring的三级缓存

小结


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

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

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

⭐扩展:Bean的创建过程:

Spring中的循环依赖问题_第1张图片

图片来源:深谈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之间的依赖关系。这样,就可以在编译时就解决循环依赖问题。

Spring中的循环依赖问题_第2张图片

图片来源: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博客


你可能感兴趣的:(spring,spring,java)