Java框架学习 —— Spring(循环依赖问题)

Java框架学习 —— Spring(循环依赖问题)循环依赖 简单来说 就是多个 bean 之间相互依赖 最终形成闭环 极限情况 甚至可能出现自己依赖自己 Componentpub class A A 中注入了 B Autowired private B b Compon

欢迎大家来到IT世界,在知识的湖畔探索吧!

循环依赖


 简单来说,就是多个bean之间相互依赖,最终形成闭环,极限情况,甚至可能出现自己依赖自己

@Component public class A { // A中注入了B @Autowired private B b; } @Component public class B { // B中也注入了A @Autowired private A a; } 

欢迎大家来到IT世界,在知识的湖畔探索吧!

Java框架学习 —— Spring(循环依赖问题)

循环依赖发生的场景

  1. 通过构造器注入循环依赖(无法解决)

当Bean通过构造器注入相互依赖时,Spring无法解决这种循环依赖,因为在构造器调用之前,Bean尚未创建,无法注入。

欢迎大家来到IT世界,在知识的湖畔探索吧!@Component public class Person { public Person(User user) {} } @Component public class User { public User(Person person){} } 

项目中如果出现此类代码,会抛出异常BeanCurrentlyInCreationException

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) 
  1. 通过属性注入循环依赖(可以解决)

通过字段(field)或setter方法注入依赖时,Spring可以通过三级缓存来解决循环依赖问题。

欢迎大家来到IT世界,在知识的湖畔探索吧!@Component public class User { @Autowired Person person; } @Component public class Person { @Autowired User user; } 

项目可以正常运行

  1. 多例Bean的属性注入循环依赖(无法解决)

对于多例(prototype)作用域的Bean,Spring默认不会在启动时初始化,而是在使用时才初始化,因此可能不会立即出现循环依赖问题,但在实际使用中可能会遇到。

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Component public class User { @Autowired Person person; } @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Component public class Person { @Autowired User user; } 

项目启动时,不会立即抛异常,因为多例的只有在被调用时,才会进行初始化,当被调用时就会抛异常

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mytest.TestSpringBean': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374) 


Spring容器的三级缓存


  • 一级缓存:存放完全初始化,且属性赋值完毕的Bean,可以直接使用
  • 二级缓存:存放早期Bean的引用,即已实例化但是未装配属性的Bean
  • 三级缓存:存放Bean工厂,即实例化Bean的工厂对象,用于解决循环依赖中Bean属性未完全装配的问题

解决循环依赖的步骤

 Spring在实例化Bean时,会先创建一个空的Bean对象,并将其放入三级缓存中,Spring开始对Bean进行属性赋值,如果发现循环依赖,会通过工厂方法提前暴露一个原始的Bean实例,并将其放入二级缓存,这样,当其他Bean引用该Bean时,可以使用这个原始实例,避免了循环依赖

简单的循环依赖(没有AOP)

Java框架学习 —— Spring(循环依赖问题)

结合了AOP的循环依赖

Java框架学习 —— Spring(循环依赖问题)

对A进行了AOP代理的话,此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象


三级缓存为什么要使用工厂而不是直接使用引用?换而言之,为什么需要这个三级缓存,直接通过二级缓存暴露一个引用不行吗?

答:这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象

Java框架学习 —— Spring(循环依赖问题)

总结

Spring 如何解决循环依赖

 Spring通过三级缓存去解决,一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象(earlySingletonObjects),三级缓存为早期曝光对象工厂(singletonFactories)。

 当A、B两个类发生循环依赖时,在A完成实例化后,就使用实例化后的对象创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的是A代理后的对象;如果A没有被AOP代理,那么这个工厂获取到的就是A的实例化对象。

 当A进行属性注入时,会去创建B,同时B又依赖于A,所以创建B的同时又会调用getBean(a)来获取需要的依赖,此时getBean(a)会从缓存中获取:第一步,先获取三级缓存中的工厂;第二步,调用对象工厂的getObject方法来获取对应对象,得到这个对象后,将其注入到B中,然后B走完它的生命周期,当B创建完后,会将B再注入到A中,此时,A在完成它的生命周期。至此,循环依赖结束


为什么要使用三级缓存呢?二级缓存能解决循环依赖吗

 如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/102503.html

(0)
上一篇 6天前
下一篇 6天前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信