0


Spring 循环依赖

优质博文:IT-BLOG-CN

一、问题现状

  1. Spring

框架中,循环依赖

  1. Circular Dependency

是指两个或多个

  1. Bean

相互依赖,形成一个循环引用。例如,

  1. Bean A

依赖于

  1. Bean B

,而

  1. Bean B

又依赖于

  1. Bean A

。这种情况可能会导致

  1. Spring

容器在创建

  1. Bean

时出现问题。如下:

  1. publicclassA{privatefinalB b;publicA(B b){this.b = b;}}publicclassB{privatefinalA a;publicB(A a){this.a = a;}}

启动时会出现如下错误:

  1. org.springframework.beans.factory.BeanCurrentlyInCreationException:Error creating bean withname'A':Requested bean is currently in creation:Is there an unresolvable circular reference?

二、解决循环依赖的方法

【1】构造器注入:构造器注入不支持循环依赖,因为在

  1. Spring

容器尝试创建

  1. Bean

时,它需要立即解析所有的构造函数参数,这会导致循环依赖问题。因此,避免使用构造器注入来解决循环依赖。

  1. @Lazy

注解可以延迟

  1. Bean

的初始化,使得

  1. Spring

在构造器注入时也能解决循环依赖问题。具体来说,

  1. @Lazy

会告诉

  1. Spring

在第一次使用

  1. Bean

时才进行初始化,而不是在容器启动时立即初始化。

  1. importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Lazy;importorg.springframework.stereotype.Component;@ComponentpublicclassA{privatefinalB b;@AutowiredpublicA(@LazyB b){this.b = b;}}@ComponentpublicclassB{privatefinalA a;@AutowiredpublicB(@LazyA a){this.a = a;}}

**【2】

  1. Setter

注入:**

  1. Setter

注入可以解决循环依赖问题,因为

  1. Spring

容器可以首先创建

  1. Bean

的实例,然后再注入依赖。

  1. publicclassA{privateB b;publicvoidsetB(B b){this.b = b;}}publicclassB{privateA a;publicvoidsetA(A a){this.a = a;}}

**【3】

  1. @Autowired

注解:** 使用

  1. @Autowired

注解进行

  1. Setter

注入或者字段注入,也可以解决循环依赖问题。

  1. publicclassA{@AutowiredprivateB b;}publicclassB{@AutowiredprivateA a;}

**【4】

  1. @Lazy

注解:** 使用

  1. @Lazy

注解可以延迟

  1. Bean

的初始化,从而解决循环依赖问题。

  1. publicclassA{@Autowired@LazyprivateB b;}publicclassB{@Autowired@LazyprivateA a;}

**【5】使用

  1. ObjectFactory

  1. Provider

:** 使用

  1. ObjectFactory

  1. Provider

可以在需要时才获取

  1. Bean

实例,从而解决循环依赖问题。

  1. publicclassA{@AutowiredprivateObjectFactory<B> bFactory;publicvoidsomeMethod(){B b = bFactory.getObject();// 使用B}}publicclassB{@AutowiredprivateObjectFactory<A> aFactory;publicvoidsomeMethod(){A a = aFactory.getObject();// 使用A}}

**【6】配置

  1. allow-circular-references: true

:** 用于控制是否允许

  1. Bean

之间的循环依赖。

  1. true

:允许

  1. Bean

之间存在循环依赖。

  1. Spring

容器会尝试通过创建

  1. Bean

的代理对象来解决循环依赖问题。这是默认行为。但从设计和架构的角度来看,尽量避免循环依赖是更好的做法。

  1. spring:main:allow-circular-references:true

三、三级缓存的组成

首先,我们要知道

  1. Spring

在创建

  1. Bean

的时候默认是按照自然排序进行创建的,所以第一步

  1. Spring

会去创建

  1. A

  1. Spring

创建

  1. Bean

的过程中分为三步:
1、实例化: 对应方法

  1. AbstractAutowireCapableBeanFactory

中的

  1. createBeanInstance

方法,简单理解就是

  1. new

了一个对象。
2、属性注入: 对应方法

  1. AbstractAutowireCapableBeanFactory

  1. populateBean

方法,为实例化中

  1. new

出来的对象填充属性。
3、初始化: 对应方法

  1. AbstractAutowireCapableBeanFactory

  1. initializeBean

,执行

  1. aware

接口中的方法,初始化方法,完成

  1. AOP

代理。

  1. Spring

是如何解决循环依赖问题的:三级缓存

三级缓存的组成
**一级缓存

  1. singletonObjects

:** 存储已经完全初始化的单例

  1. Bean

。类型

  1. ConcurrentHashMap<String, Object>

**二级缓存

  1. earlySingletonObjects

:** 多了一个

  1. early

,表示缓存的是早期的

  1. bean

对象。早期是什么意思?表示

  1. Bean

的生命周期还没走完就把这个

  1. Bean

放入

  1. earlySingletonObjects

,通常是为了避免循环依赖。类型

  1. ConcurrentHashMap<String, Object>

**三级缓存

  1. singletonFactories

:** 存储创建

  1. Bean

的工厂

  1. ObjectFactory

,用于解决循环依赖。类型:

  1. ConcurrentHashMap<String, ObjectFactory<?>>
  1. /** Cache of singleton objects: bean name --> bean instance */privatefinalMap<String,Object> singletonObjects =newConcurrentHashMap<String,Object>(256);/** Cache of singleton factories: bean name --> ObjectFactory */privatefinalMap<String,ObjectFactory<?>> singletonFactories =newHashMap<String,ObjectFactory<?>>(16);/** Cache of early singleton objects: bean name --> bean instance */privatefinalMap<String,Object> earlySingletonObjects =newHashMap<String,Object>(16);

四、三级缓存的工作原理

**创建

  1. Bean

实例:** 当

  1. Spring

容器创建一个

  1. Bean

时,首先会尝试从一级缓存

  1. singletonObjects

中获取该

  1. Bean

。如果获取不到,再尝试从二级缓存

  1. earlySingletonObjects

中获取。如果仍然获取不到,再尝试从三级缓存

  1. singletonFactories

中获取。

  1. protectedObjectgetSingleton(String beanName,boolean allowEarlyReference){Object singletonObject =this.singletonObjects.get(beanName);if(singletonObject ==null&&isSingletonCurrentlyInCreation(beanName)){synchronized(this.singletonObjects){
  2. singletonObject =this.earlySingletonObjects.get(beanName);if(singletonObject ==null&& allowEarlyReference){ObjectFactory<?> singletonFactory =this.singletonFactories.get(beanName);if(singletonFactory !=null){
  3. singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return(singletonObject != NULL_OBJECT ? singletonObject :null);}

**提前曝光

  1. Bean

:** 在创建

  1. Bean

的过程中,如果检测到循环依赖,

  1. Spring

会提前将该

  1. Bean

的一个早期引用(通常是通过

  1. ObjectFactory

创建的代理对象)放入三级缓存

  1. singletonFactories

中。

解决循环依赖: 当另一个

  1. Bean

需要依赖这个尚未完全初始化的

  1. Bean

时,会从三级缓存

  1. singletonFactories

中获取该

  1. Bean

的早期引用,并将其放入二级缓存

  1. earlySingletonObjects

中。

完成初始化: 一旦

  1. Bean

完全初始化完成,

  1. Spring

会将其从二级缓存

  1. earlySingletonObjects

中移除,并放入一级缓存

  1. singletonObjects

中。

案例:如上有两个类

  1. A

  1. B

,它们通过构造器注入互相依赖:
**【1】创建

  1. A

的实例:**

  1. A

依赖

  1. B

,但

  1. B

尚未创建。

  1. Spring

会将

  1. A

的早期引用(通常是一个代理对象)放入三级缓存

  1. singletonFactories

中。
**【2】创建

  1. B

的实例:**

  1. B

依赖

  1. A

  1. Spring

会从三级缓存

  1. singletonFactories

中获取

  1. A

的早期引用,并将其放入二级缓存

  1. earlySingletonObjects

中。通过早期引用可知,

  1. B

注入的是

  1. A

的引用,所以最终拿到的是一个完整的

  1. A

对象。
**【3】完成

  1. B

的初始化:**

  1. B

完全初始化后,放入一级缓存

  1. singletonObjects

中。
**【4】完成

  1. A

的初始化:**

  1. A

获取到

  1. B

的完全初始化的实例后,完成自身初始化,并放入一级缓存

  1. singletonObjects

中。

五、spring 循环依赖为什么使用三级缓存而不是二级缓存

【1】代理对象的创建: 在某些情况下,

  1. Spring

需要为

  1. bean

创建代理对象(例如,使用

  1. AOP

时)。代理对象的创建通常在

  1. bean

初始化的后期阶段进行。

如果只使用二级缓存,意味着所有

  1. Bean

在实例化后就要完成

  1. AOP

代理,在某些情况下,

  1. Spring

可能无法正确地创建代理对象,因为代理对象的创建依赖于完整的

  1. bean

初始化过程,这样违背了

  1. Spring

设计的原则,

  1. Spring

在设计之初就是通过

  1. AnnotationAwareAspectJAutoProxyCreator

这个后置处理器来在Bean生命周期的最后一步来完成

  1. AOP

代理,而不是在实例化后就立马进行

  1. AOP

代理。

三级缓存中的对象工厂可以确保在需要时创建代理对象,并将其放入二级缓存,从而确保代理对象可以在循环依赖中正确地被引用。

【2】延迟创建早期引用: 三级缓存中的对象工厂允许

  1. Spring

在需要时延迟创建早期引用,而不是立即创建。这种延迟创建机制可以确保在某些特殊情况下,

  1. bean

可以在完全初始化之前被引用。

通过这种方式,

  1. Spring

可以更灵活地处理各种复杂的依赖关系和代理对象的创建。

标签: spring java 数据库

本文转载自: https://blog.csdn.net/zhengzhaoyang122/article/details/142691283
版权归原作者 程序猿进阶 所有, 如有侵权,请联系我们删除。

“Spring 循环依赖”的评论:

还没有评论