0


Spring 循环依赖

优质博文:IT-BLOG-CN

一、问题现状

Spring

框架中,循环依赖

Circular Dependency

是指两个或多个

Bean

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

Bean A

依赖于

Bean B

,而

Bean B

又依赖于

Bean A

。这种情况可能会导致

Spring

容器在创建

Bean

时出现问题。如下:

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

启动时会出现如下错误:

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

二、解决循环依赖的方法

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

Spring

容器尝试创建

Bean

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

@Lazy

注解可以延迟

Bean

的初始化,使得

Spring

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

@Lazy

会告诉

Spring

在第一次使用

Bean

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

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】

Setter

注入:**

Setter

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

Spring

容器可以首先创建

Bean

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

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

**【3】

@Autowired

注解:** 使用

@Autowired

注解进行

Setter

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

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

**【4】

@Lazy

注解:** 使用

@Lazy

注解可以延迟

Bean

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

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

**【5】使用

ObjectFactory

Provider

:** 使用

ObjectFactory

Provider

可以在需要时才获取

Bean

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

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

**【6】配置

allow-circular-references: true

:** 用于控制是否允许

Bean

之间的循环依赖。

true

:允许

Bean

之间存在循环依赖。

Spring

容器会尝试通过创建

Bean

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

spring:main:allow-circular-references:true

三、三级缓存的组成

首先,我们要知道

Spring

在创建

Bean

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

Spring

会去创建

A

Spring

创建

Bean

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

AbstractAutowireCapableBeanFactory

中的

createBeanInstance

方法,简单理解就是

new

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

AbstractAutowireCapableBeanFactory

populateBean

方法,为实例化中

new

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

AbstractAutowireCapableBeanFactory

initializeBean

,执行

aware

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

AOP

代理。

Spring

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

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

singletonObjects

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

Bean

。类型

ConcurrentHashMap<String, Object>

**二级缓存

earlySingletonObjects

:** 多了一个

early

,表示缓存的是早期的

bean

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

Bean

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

Bean

放入

earlySingletonObjects

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

ConcurrentHashMap<String, Object>

**三级缓存

singletonFactories

:** 存储创建

Bean

的工厂

ObjectFactory

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

ConcurrentHashMap<String, ObjectFactory<?>>
/** 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);

四、三级缓存的工作原理

**创建

Bean

实例:** 当

Spring

容器创建一个

Bean

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

singletonObjects

中获取该

Bean

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

earlySingletonObjects

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

singletonFactories

中获取。

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

**提前曝光

Bean

:** 在创建

Bean

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

Spring

会提前将该

Bean

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

ObjectFactory

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

singletonFactories

中。

解决循环依赖: 当另一个

Bean

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

Bean

时,会从三级缓存

singletonFactories

中获取该

Bean

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

earlySingletonObjects

中。

完成初始化: 一旦

Bean

完全初始化完成,

Spring

会将其从二级缓存

earlySingletonObjects

中移除,并放入一级缓存

singletonObjects

中。

案例:如上有两个类

A

B

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

A

的实例:**

A

依赖

B

,但

B

尚未创建。

Spring

会将

A

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

singletonFactories

中。
**【2】创建

B

的实例:**

B

依赖

A

Spring

会从三级缓存

singletonFactories

中获取

A

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

earlySingletonObjects

中。通过早期引用可知,

B

注入的是

A

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

A

对象。
**【3】完成

B

的初始化:**

B

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

singletonObjects

中。
**【4】完成

A

的初始化:**

A

获取到

B

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

singletonObjects

中。

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

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

Spring

需要为

bean

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

AOP

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

bean

初始化的后期阶段进行。

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

Bean

在实例化后就要完成

AOP

代理,在某些情况下,

Spring

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

bean

初始化过程,这样违背了

Spring

设计的原则,

Spring

在设计之初就是通过

AnnotationAwareAspectJAutoProxyCreator

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

AOP

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

AOP

代理。

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

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

Spring

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

bean

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

通过这种方式,

Spring

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

标签: spring java 数据库

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

“Spring 循环依赖”的评论:

还没有评论