Spring系列第28篇:Bean循环依赖详解,阿里巴巴Java编程笔试题

本文深入探讨了Spring中Bean的循环依赖问题,包括如何检测和解决单例及非单例Bean的循环依赖。通过源码分析,阐述了Spring如何利用3级缓存来处理单例Bean的循环依赖,以及为何非单例Bean无法通过这种方式解决。文章还列举了不同情况下Bean循环依赖的处理结果,并提供了相关示例代码。
摘要由CSDN通过智能技术生成

下面我们来一个个突破。

什么是循环依赖?


这个很好理解,多个bean之间相互依赖,形成了一个闭环。

比如:A依赖于B、B依赖于C、C依赖于A。

代码中表示:

public class A{

B b;

}

public class B{

C c;

}

public class C{

A a;

}

如何检测是否存在循环依赖?


检测循环依赖比较简单,使用一个列表来记录正在创建中的bean,bean创建之前,先去记录中看一下自己是否已经在列表中了,如果在,说明存在循环依赖,如果不在,则将其加入到这个列表,bean创建完毕之后,将其再从这个列表中移除。

源码方面来看一下,spring创建单例bean时候,会调用下面方法

protected void beforeSingletonCreation(String beanName) {

if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {

throw new BeanCurrentlyInCreationException(beanName);

}

}

singletonsCurrentlyInCreation就是用来记录目前正在创建中的bean名称列表,this.singletonsCurrentlyInCreation.add(beanName)返回false,说明beanName已经在当前列表中了,此时会抛循环依赖的异常BeanCurrentlyInCreationException,这个异常对应的源码:

public BeanCurrentlyInCreationException(String beanName) {

super(beanName,

“Requested bean is currently in creation: Is there an unresolvable circular reference?”);

}

上面是单例bean检测循环依赖的源码,再来看看非单例bean的情况。

以prototype情况为例,源码位于org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中,将主要代码列出来看一下:

//检查正在创建的bean列表中是否存在beanName,如果存在,说明存在循环依赖,抛出循环依赖的异常

if (isPrototypeCurrentlyInCreation(beanName)) {

throw new BeanCurrentlyInCreationException(beanName);

}

//判断scope是否是prototype

if (mbd.isPrototype()) {

Object prototypeInstance = null;

try {

//将beanName放入正在创建的列表中

beforePrototypeCreation(beanName);

prototypeInstance = createBean(beanName, mbd, args);

}

finally {

//将beanName从正在创建的列表中移除

afterPrototypeCreation(beanName);

}

}

Spring如何解决循环依赖的问题


这块建议大家先看一下:详解bean生命周期

spring创建bean主要的几个步骤:

  1. 步骤1:实例化bean,即调用构造器创建bean实例

  2. 步骤2:填充属性,注入依赖的bean,比如通过set方式、@Autowired注解的方式注入依赖的bean

  3. 步骤3:bean的初始化,比如调用init方法等。

从上面3个步骤中可以看出,注入依赖的对象,有2种情况:

  1. 通过步骤1中构造器的方式注入依赖

  2. 通过步骤2注入依赖

先来看构造器的方式注入依赖的bean,下面两个bean循环依赖

@Component

public class ServiceA {

private ServiceB serviceB;

public ServiceA(ServiceB serviceB) {

this.serviceB = serviceB;

}

}

@Component

public class ServiceB {

private ServiceA serviceA;

public ServiceB(ServiceA serviceA) {

this.serviceA = serviceA;

}

}

构造器的情况比较容易理解,实例化ServiceA的时候,需要有serviceB,而实例化ServiceB的时候需要有serviceA,构造器循环依赖是无法解决的,大家可以尝试一下使用编码的方式创建上面2个对象,是无法创建成功的!

再来看看非构造器的方式注入相互依赖的bean,以set方式注入为例,下面是2个单例的bean:serviceA和serviceB:

@Component

public class ServiceA {

private ServiceB serviceB;

@Autowired

public void setServiceB(ServiceB serviceB) {

this.serviceB = serviceB;

}

}

@Component

public class ServiceB {

private ServiceA serviceA;

@Autowired

public void setServiceA(ServiceA serviceA) {

this.serviceA = serviceA;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值