spring循环依赖过程解析

ゞ 浴缸里的玫瑰 2023-05-30 09:27 230阅读 0赞

spring解决循环依赖

spring在创建和依赖注入单例对象时,通过三级缓存的设计,可以解决大多数的循环依赖问题,但是基于三级缓存的解决方案,对于部分场景还是无法解决循环依赖的问题,会由spring抛出异常。

spring的三级缓存由singletonObjectsearlySingletonObjectssingletonFactories组成:

  • singletonObjects:保存初始化完成的单例bean实例;
  • earlySingletonObjects:保存提前曝光的单例bean实例;
  • singletonFactories:保存单例bean的工厂函数对象;

对于通常的A依赖B、B依赖A的简单循环依赖的场景,spring的单例对象创建和依赖注入的流程如下图:

  1. @Service
  2. public class A {
  3. @Autowired
  4. private B b;
  5. public A() {
  6. }
  7. }
  8. @Service
  9. public class B {
  10. @Autowired
  11. private A a;
  12. public B() {
  13. }
  14. }

在这里插入图片描述

构造方法注入导致循环依赖

成功

对于相互依赖的Bean,如果全部都是构造方法依赖注入,spring必然无法解决这种循环依赖。但是如果如下场景,A通过@Autowired依赖B,而B通过构造方法依赖A,spring可以成功解决循环依赖。

  1. @Service
  2. public class A {
  3. @Autowired
  4. private B b;
  5. public A() {
  6. }
  7. }
  8. @Service
  9. public class B {
  10. private A a;
  11. public B(A a) {
  12. this.a = a;
  13. }
  14. }

在这里插入图片描述

循环依赖异常

但是对于这种部分Bean通过构造方法依赖注入的场景,spring的三级缓存并不是百试百灵。把上面成功依赖注入的A和B相互换位,A通过构造方法依赖B,而B通过@Autowired依赖A,则会出现spring无法解决循环依赖而抛出循环依赖的异常。

并没有修改依赖复杂度,仅仅是A与B相互换了依赖位置,spring就抛出了循环依赖异常,其中的流程如下图:

  1. @Service
  2. public class A {
  3. private B b;
  4. public A(B b) {
  5. this.b = b;
  6. }
  7. }
  8. @Service
  9. public class B {
  10. @Autowired
  11. private A a;
  12. public B() {
  13. }
  14. }
  15. ***************************
  16. APPLICATION FAILED TO START
  17. ***************************
  18. Description:
  19. The dependencies of some of the beans in the application context form a cycle:
  20. ┌─────┐
  21. | a defined in file [D:\Program\workspace\other\springboot\target\classes\com\wakzz\autowire\A.class]
  22. | b (field private com.wakzz.autowire.A com.wakzz.autowire.B.a)
  23. └─────┘

在这里插入图片描述

代理导致循环依赖

@Transactional代理成功

spring经常会对创建的单例对象进行代理,对于代理对象的依赖注入与非代理对象依赖注入有稍微的区别。例如通过@Transactional注解代理A后的依赖注入流程如下图:

  1. @Transactional
  2. @Service
  3. public class A {
  4. @Autowired
  5. private B b;
  6. public A() {
  7. }
  8. }
  9. @Service
  10. public class B {
  11. @Autowired
  12. private A a;
  13. public B() {
  14. }
  15. }

在这里插入图片描述

@Async代理循环依赖异常

然而并不是每个代理对象的依赖注入流程都是一样的,典型的例子就是@Async注解,@Async经常会造成循环依赖的问题。将上面成功代理依赖注入的案例中的@Transactional注解换成@Async注解,应用启动后spring就会抛出BeanCurrentlyInCreationException异常,该流程如下图:

  1. @Async
  2. @Service
  3. public class A {
  4. @Autowired
  5. private B b;
  6. public A() {
  7. }
  8. }
  9. @Service
  10. public class B {
  11. @Autowired
  12. private A a;
  13. public B() {
  14. }
  15. }
  16. org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
  17. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  18. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  19. at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  20. at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  21. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  22. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  23. at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  24. at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  25. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
  26. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
  27. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
  28. at org.springframework.boot.SpringApplication.run(SpringApplication.java:330) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
  29. at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:137) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
  30. at com.wakzz.Application.main(Application.java:20) [classes/:na]

在这里插入图片描述

有图可见,@Async@Transactional在对象创建和依赖注入的流程中的区别,仅仅是Bean对象被代理成代理对象的时机。@Transactional的代理是当从单例工程获取对象时,由单例工厂完成单例对象的代理;而@Async的代理则是在Bean对象依赖注入完成之后,再初始化并代理成代理对象。

@Async代理成功

跟部分对象构造方法注入的场景一样,不需要修改依赖复杂度,仅仅是修改了A与B的依赖关系,让被代理的对象后一步创建,就可以解决该依赖关系成功启动应用。spring的流程如下图:

  1. @Service
  2. public class A {
  3. @Autowired
  4. private B b;
  5. public A() {
  6. }
  7. }
  8. @Async
  9. @Service
  10. public class B {
  11. @Autowired
  12. private A a;
  13. public B() {
  14. }
  15. }

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,230人围观)

还没有评论,来说两句吧...

相关阅读

    相关 spring循环依赖

    Bean的生命周期 这里不会对Bean的生命周期进行详细的描述,只描述一下大概的过程。 Bean的生命周期指的就是:在Spring中,Bean是如何生成的? > 被Sp