Spring循环依赖

1.Bean实例化、循环依赖

1.1 什么是循环依赖?

如下有 A、B、C 三个类,可以看到发生了循环依赖:彼此互相依赖,导致各自都需要对方的依赖,形成依赖闭环。

但是我们会发现field属性注入、setter方法注入的循环依赖:即使发生了循环依赖,我们依然可以启动,使用并没有任何影响。这种方式是我们最为常用的依赖注入方式,Spring会解决field属性注入、setter方法注入的循环依赖。但是无法解决构造器注入的循环依赖。

Spring IoC 容器会在运行时检测到构造函数注入循环引用,并抛出 BeanCurrentlyInCreationException。从而提醒你避免循环依赖。所以要避免构造函数注入,可以使用 setter 注入替代。根据官方文档说明,Spring 会自动解决基于 setter 注入的循环依赖。当然在咱们工作中现在都使用 @Autowired 注解来注入属性。 @Autowired 是通过反射进行赋值。

依赖注入的方式:

  1. 注解注入:@Autowire和@Resource:这种注解可以直接解决循环依赖问题,不需要额外处理
  2. 构造方法器注入:构造方法注入需要使用@Lazy 注解来作用于循环依赖的属性
  3. setter注入:setter注入也可以直接解决循环依赖问题,不需要额外处理

Spring只是解决了单例模式下属性依赖的循环问题;Spring为了解决单例的循环依赖问题,使用了三级缓存。

2.Spring的循环依赖

现在有循环依赖代码如下:

1
2
3
4
5
6
7
8
9
10
11
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}

@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}

无论先创建ServiceA还是ServiceB时,都会发生循环依赖!

1
2
3
4
5
6
7
// 自己依赖自己
@Service
public class ServiceA {

@Autowired
private ServiceA serviceA;
}

自己依赖自己其实也是循环依赖的一种

2.1 实例化与初始化

在介绍spring如何解决循环依赖前,我们必须先了解一个完整的对象其实包含两部分:当前对象实例化和对象属性的实例化:

  • 类的实例化:

    是指创建一个对象的过程。这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。(就是调用构造函数,生成一个对象。)

  • 类的初始化:

是完成程序执行前的准备工作。在这个阶段,*静态的*(变量,方法,代码块)会被执行。同时在会开辟一块存储空间用来存放静态的数据。初始化只在类加载的时候执行一次(为变量设置值)

这个过程可以按照如下方式进行理解:

在Spring中,对象的实例化是通过反射实现的,而对象的属性则是在对象实例化之后通过一定的方式设置的。

2.2 解决循环依赖

在spring中,通过三级缓存来解决循环依赖的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
/** Cache of singleton objects: bean name --> bean instance(一级缓存,存储单例对象,Bean 已经实例化,初始化完成) */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

/** Cache of early singleton objects: bean name --> bean instance(二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化) */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** Cache of singleton factories: bean name --> ObjectFactory(三级缓存,单例的工厂Bean缓存集合) */
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);

/** Names of beans that are currently in creation 正在创建的对象*/
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));

  • singletonObjects」:缓存某个 beanName 对应的经过了完整生命周期的bean;

  • earlySingletonObjects」:缓存提前拿原始对象进行了 AOP 之后得到的代理对象,原始对象还没有进行属性注入和后续的 BeanPostProcesso r等生命周期;

  • singletonFactories」:缓存的是一个 ObjectFactory ,主要用来去生成原始对象进行了 AOP之后得到的「代理对象」,在每个 Bean 的生成过程中,都会提前暴露一个工厂,这个工厂可能用到,也可能用不到,如果没有出现循环依赖依赖本 bean,那么这个工厂无用,本 bean 按照自己的生命周期执行,执行完后直接把本 bean 放入 singletonObjects 中即可,如果出现了循环依赖依赖了本 bean,则另外那个 bean 执行 ObjectFactory 提交得到一个 AOP 之后的代理对象(如果有 AOP 的话,如果无需 AOP ,则直接得到一个原始对象

spring容器的实现从根源上来看的话是通过BeanFactory实现的,但是BeanFactory只是一个接口类,真正作为一个可以独立使用的容器还是通过DeafultListableBeanFactory实现的,DefaultListableBeanFactory结构图解:

3.源码角度

3.1 DefaultListableBeanFactory的preInstantiateSingletons方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public void preInstantiateSingletons() throws BeansException {
... ...
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
//获取指定名称的Bean定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//Bean不是抽象的,是单态模式的,且lazy-init属性配置为false
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
... ...
if (isEagerInit) {
//调用getBean方法,触发容器对Bean实例化和依赖注入过程
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}

最终都会调用getBean方法,触发容器对Bean实例化和依赖注入过程。getBean方法是在Beanfactory中的一个接口,由AbstractBeanFactory实现,执行doGetBean方法,在Spring中,凡是以do开头的方法一般都是细节上的逻辑处理,也就是具体的实现代码。

1
2
3
4
5
6
//获取IOC容器中指定名称的Bean
@Override
public Object getBean(String name) throws BeansException {
//doGetBean才是真正向IoC容器获取被管理Bean的过程
return doGetBean(name, null, null, false);
}

在spring中,想要使用对象最终都会调用AbstractBeanFactorygetBean()方法,进而执行doGetBean()方法。

在spring中,凡是以do开头的方法一般都是细节上的逻辑实现,所以在doGetBean()中执行了DefaultSingletonBeanRegistrygetSingleton()方法。

3.2 AbstractBeanFactory的doGetBean方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//1、转换beanName,返回 bean 名称,必要时去除工厂取消引用前缀,并将别名解析为规范名称
final String beanName = transformedBeanName(name);
Object bean;

Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 有缓存,如果指定名称的Bean在容器中已有单例模式的Bean被创建。直接返回已经创建的Bean
} else {
... ...
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 父 factory 里加载
}

if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}

//2、转换成RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

//依赖,先不考虑
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
//...
}

// Create bean instance.
if (mbd.isSingleton()) {
//3、关键:单例类的初始化
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
//prototype ...
}
else {
// 其他scope ...
}
}

// Check if required type matches the type of the actual bean instance.
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
// 如果类型不一致,做类型转换
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
return (T) bean;
}

3.3 DefaultSingletonBeanRegistry的getSingleton(String beanName, boolean allowEarlyReference)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
----> Object sharedInstance = this.getSingleton(beanName);

//返回在给定名称下注册的(原始)单例对象。 <p>检查已经实例化的单例,并允许提前引用当前创建的单例(解决循环引用)
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
/**
* 如果singletonObjects还没有此bean,有两种情况
* 1.这个bean正在创建状态,先从earlySingletonObjects获取
* 2.这个bean还没开始创建
*/
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
/**
* 如果earlySingletonObjects还没有此bean,有两种情况
* 1.说明还未被其他bean注入,正在创建状态,先从singletonFactories获取
* 2.该bean还没开始创建
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/*
*将此bean放到提前缓存到earlySingletonObjects中
* 从singletonObject删除bean
*/
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

这个方法做了这么一件事,

  1. 先到singletonObjects中获取,如果有表示实例化已经完成;

  2. 否则到earlySingletonObjects获取,如果有表示已经有bean,且存在循环依赖,将此bean作为属性注入了

  3. 否则到singletonFactories获取,如果存在循环依赖,且此属性是第一次被其他bean作为属性

3.4 DefaultSingletonBeanRegistry的getSingleton(String beanName, ObjectFactory<?> singletonFactory)

上面的代码主要就是执行一些条件判断,重要的是单例类的getSingleton()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Create bean instance.
if (mbd.isSingleton()) {
//3、关键:单例类的初始化
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

其中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
---> sharedInstance = getSingleton(beanName, new ObjectFactory<Object>()

//返回以给定名称注册的(原始)单例对象,如果尚未注册,则创建并注册一个新对象
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
... ...
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (
... ...
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

在这个getSingleton中,还会再去从一级缓存中获取一次,如果有则直接返回,没有则在beforeSingletonCreation将要创建的对象存入 set 集合中。同理,还有afterSingletonCreation,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}

protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
----->
//当前正在创建的bean的名称
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));

由此可知:创建的时候存在singletonsCurrentlyInCreation队列里,创建成功则被移除。

最后回到在3.4第一哥代码块中执行下列代码:

1
return createBean(beanName, mbd, args)

方法中进行Bean的创建,AbstractAutowireCapableBeanFactory中的createBean方法。

3.5 AbstractAutowireCapableBeanFactory的createBean方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//此类的中心方法:创建 bean 实例、填充 bean 实例、应用后处理器等
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
... ...
try {
//创建Bean的入口
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
... ...
}

3.6 AbstractAutowireCapableBeanFactory的doCreateBean

主要是进行了一些判断处理,重要方法就是其中的doCreateBean,里面是执行创建对象的细节.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//真正创建Bean的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

// Instantiate the bean.
//封装被创建的Bean对象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//createBeanInstance---->//创建Bean的实例对象,返回BeanWrapper
//protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {}

if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//得到bean
final Object bean = instanceWrapper.getWrappedInstance();
... ...

//判断是否有循环依赖,是否需要暴露对象的引用
/* Eagerly cache singletons to be able to resolve circular references
*even when triggered by lifecycle interfaces like BeanFactoryAware.
* 向容器中缓存单例模式的Bean对象,以防循环引用
* 单例
* 允许循环依赖
* 当前Bean正在创建中,检测到了循环依赖
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
... ...
//这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
//getEarlyBeanReference方法作用:获取对指定bean的早期访问的引用,通常用于解析循环引用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// Initialize the bean instance.
//Bean对象的初始化,依赖注入在此触发
//这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
Object exposedObject = bean;
try {
//将Bean实例对象封装,并且Bean定义中配置的属性值赋值给实例对象
populateBean(beanName, mbd, instanceWrapper);
//初始化Bean对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
... ...

// Register bean as disposable.
//注册完成依赖注入的Bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}

return exposedObject;
}

其中的addSingletonFactory方法:判断是否是循环引用,是的话执行下面方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---> addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));


protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//添加到三级缓存
this.singletonFactories.put(beanName, singletonFactory);
//消除此Bean在二级缓存里的缓存信息
this.earlySingletonObjects.remove(beanName);
//这里为了记录注册单例的顺序
this.registeredSingletons.add(beanName);
}
}
}

消除此Bean在二级缓存里的缓存信息,将其包装成singletonFactory实例往三级缓存里添加,判断是否是循环引用,是的话需要添加到三级缓存中。

这里有一个重点就是Spring解决循环依赖的真相就在这一段源码中:在这里beanFactory被put进了singletonFactories,此时的bean只是完成了初始化构造的bean,还没有进行set或者注解注入的bean,是bean的一个中间状态,但是已经能被识别出来了,所以Spring此时将这个对象提前曝光出来让大家认识、使用。

在3.3 节中,getSingleton(String beanName, boolean allowEarlyReference) 方法中,如果

1
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) 

则:

1
2
3
4
5
6
7
/*
*将此bean放到提前缓存到earlySingletonObjects中
* 从singletonObject删除bean
*/
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);

4.Spring循环依赖解决

以上面为例子,ADefaultListableBeanFactorypreInstantiateSingletons中,调用AbstractBeanFactory的getBean方法,然后调用doGetBean方法,进入方法的时候会调用getSingleton方法,会进行如下判断:

  • 先到singletonObjects中获取,如果有表示实例化已经完成;
  • 到earlySingletonObjects获取,如果没有则找三级缓存,如果有表示已经有bean,且存在循环依赖,返回对象。
  • 到singletonFactories获取,如果有则返回对象,且将对象存入二级缓存,三级缓存将此对象清除。

此处A在这三个缓存中都获取不到,然后就创建对象,在创建的时候还会再去从一级缓存中获取一次,如果有则直接返回,没有则先在beforeSingletonCreation将要创建的对象存入 set 集合中,因此是不可重复的,然后执行AbstractAutowireCapableBeanFactory中的createBean方法中的doCreateBean方法,所有的对象都封装成BeanWrapper对象,通过BeanWrapper的.getWrappedInstance()得到具体的实例,然后再根据条件判断

1
2
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));

是否从二级缓存移除,存入三级缓存,此处A已经存入三级缓存。

B的过程和 A 的一样,也是创建了三级缓存,然后去创建 C,同理,C也进入三级缓存,这时候三级缓存里面有它们三个的 singletonFactory 。C 也调用到 doGetBean 方法去获取 A.

填充属性ServiceA的时候,这时候能够从三级缓存中拿到半成品的ObjectFactory,拿到ObjectFactory对象后,调用ObjectFactory.getObject()方法最终会调用getEarlyBeanReference()方法,getEarlyBeanReference这个方法主要逻辑大概描述下如果bean被AOP切面代理则返回的是beanProxy对象,如果未被代理则返回的是原bean实例。

不过这次C调用到 Object sharedInstance = getSingleton(beanName); 的时候, A 已经存在了。这次调用虽然没有从一级缓存 (singletonObjects) 中获取到 A,但是 A创建中,所以进入判断在这里执行完之后,A 从三级缓存升级到二级缓存。

这里获取到的是 A 的引用,注意 A 这时候还没创建完成,只是引用。所以这里赋值的是 A 的引用。

填充属性的时候,spring会提前将已经实例化的bean通过ObjectFactory半成品暴露出去,为什么称为半成品是因为这时候的bean对象实例化,但是未进行属性填充,是一个不完整的bean实例对象,这时我们会发现能够拿到bean实例(属性未填充),然后从三级缓存移除,放到二级缓存earlySingletonObjects中,而此时C注入的是一个半成品的实例A对象,不过随着C初始化完成后,A会继续进行后续的初始化操作,最终C会注入的是一个完整的A实例,因为在内存中它们是同一个对象。

到这里 C 就创建完了。如上所示,C 创建完成之后,会执行addSingleton方法,然后会将 C 添加到一级缓存和已注册列表中,同时从二级三级缓存中删除 C。继续执行 BA 的属性赋值以及后续的初始化流程。最后 BA 都进入一级缓存。至此,循环依赖解决完毕。

对于“prototype”作用域bean, Spring 容器无法完成依赖注入,因为Spring 容器不进行缓存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean 。

5.半成品"Bean"

半成品的bean可能不太理解,我们可以看下面一段代码:

1
2
3
4
5
6
public class Student {

private String number;
private String age;
private String name;
}

main方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
    public static void main(String[] args) {
Student student = new Student();
System.out.println(student);

student.setNumber("01");
student.setAge("18");
student.setName("zhangsan");
System.out.println(student);
}

--->
com.example.demo.aaa.Student@36baf30c
com.example.demo.aaa.Student@36baf30c

如上,打印的地址是一样的,第一次打印的就是所谓的"半成品"。虽然它此时的属性没有赋值,但是它是可以使用(被别人引用)的半成品。在最终使用的时候,它经过一系列执行,注入的就是成熟的bean对象。

6.为什么要使用三级缓存,仅仅使用二级缓存行吗?

使用 earlySingletonObjects 和 singletonObjects 两级缓存,一个存放早期对象,一个存放初始化完成后的对象,也能实现同样的功能,singletonFactories 好像显得有些多此一举。其实不是的,对于普通对象,确实只要返回刚创建完的早期对象就好了,二级缓存已经可以解决循环依赖。

但如果A 的原始对象进行了 AOP 产生了一个代理对象,此时就会出现,对于 A 而言,如果仅有二级缓存,它的 Bean 对象其实应该是 AOP 之后的代理对象,而此时的B 的 a 属性对应的并不是 AOP 之后的代理对象,这就产生了冲突:B 依赖的 A 和最终的 A 不是同一个对象,因此才出现三级缓存, singletonFactories(三级缓存) 根据 beanName 得到一个 ObjectFactory ,然后执行 ObjectFactory ,也就是执行 getEarlyBeanReference 方法,此时会得到一个 A 原始对象经过 AOP 之后的代理对象,然后把该代理对象放入 earlySingletonObjects 中。后续为B所用,此时得到的A都是代理对象。

链接:https://segmentfault.com/a/1190000023647227

总结:

Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理,如果只有二级缓存,这样违背了Spring设计原则。Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后----通过AnnotationAwareAspectJAutoProxyCreator这个---后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。 Spring 需要三级缓存的目的是为了在没有循环依赖的情况下,延迟代理对象的创建,使 Bean 的创建符合 Spring 的设计原则。

7.为什么Spring无法解决构造器的循环依赖?

在spring中,springIOC容器在运行时检测到构造函数注入循环依赖则直接抛出异常,因此要避免构造器的循环依赖而使用setter注入。spring也会自动解决基于setter注入的循环依赖。

Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态。而构造器是完成实例化的,所以构造器的循环依赖Spring并没有解决,bean都是需要可以先被实例化才可以的,所以这也就是为什么构造器依赖可能会失败的原因。假如A构造器依赖B,因为实例化A需要先调用A的构造函数,发现依赖B,那么需要去初始化B,但是B也依赖A,不管B是通过构造器注入还是setter注入,此时由于A没有被实例化,没有放入三级缓存,所以B无法被初始化,所以spring会直接报错。反之,如果A通过setter注入的话,那么则可以通过构造函数先实例化,放入缓存,然后再填充属性,这样的话不管B是通过setter还是构造器注入A,都能在缓存中获取到,于是可以初始化。

解决办法:

​ Spring构造器注入循环依赖的解决方案是@Lazy,其基本思路是:对于强依赖的对象,一开始并不注入对象本身,而是注入其代理对象,以便顺利完成实例的构造,形成一个完整的对象,这样与其它应用层对象就不会形成互相依赖的关系;当需要调用真实对象的方法时,通过TargetSource去拿到真实的对象[DefaultListableBeanFactory#doResolveDependency],然后通过反射完成调用

8.什么情况下循环依赖可以被处理?

首先要明确一点,Spring解决循环依赖是有前置条件的

  1. 出现循环依赖的Bean必须要是单例
  2. 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)

其中第一点应该很好理解,第二点:不能全是构造器注入是什么意思呢?我们还是用代码说话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class ServiceA {

private ServiceA(ServiceB b){

}
}

@Component
public class ServiceB {

private ServiceB(ServiceA a){

}
}

在上面的例子中,A中注入B的方式是通过构造器,B中注入A的方式也是通过构造器,这个时候循环依赖是无法被解决,如果你的项目中有两个这样相互依赖的Bean,在启动时就会报出以下错误:

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?

如下四种情况的循环依赖测试

依赖情况 依赖注入方式 循环依赖是否被解决
AB相互依赖(循环依赖) 均采用setter方法注入
AB相互依赖(循环依赖) 均采用属性自动注入
AB相互依赖(循环依赖) 均采用构造器注入
AB相互依赖(循环依赖) A中注入B的方式为setter方法,B中注入A的方式为构造器
AB相互依赖(循环依赖) B中注入A的方式为setter方法,A中注入B的方式为构造器

不是只有在setter方法注入的情况下循环依赖才能被解决,即使存在构造器注入的场景下,循环依赖依然被可以被正常处理掉。

所以日常所说的只解决setter注入方式是错误的。spring解决循环依赖是不能全是构造器注入的方式。

为什么在下表中的第四种情况的循环依赖能被解决,而第五种情况不能被解决呢?

是因为Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建,A会首先暴露出来。然后才回去注入B,B再注入A的时候,就可以通过三级缓存拿到A了。此时的A是一个实例化的一个中间状态的Bean.

反之:如果使用如果A先使用构造器,在注入的时候,他会去找B,B再注入A,可此时A并没有暴露(因为还没有实例化成功),也就失败了。

9.Read more

:lollipop::https://developer.aliyun.com/article/766880

:lollipop::https://juejin.cn/post/6985337310472568839

:lollipop::https://segmentfault.com/a/1190000023647227

:lollipop::https://blog.csdn.net/weixin_48777366/article/details/123645686

:lollipop::https://pdai.tech/md/spring/spring-x-framework-ioc-source-3.html

:lollipop::https://www.zhihu.com/question/438247718/answer/1730527725

:lollipop::https://juejin.cn/post/6844903843596107790


博客说明

文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,不用于任何的商业用途。如有侵权,请联系本人删除。谢谢!


Spring循环依赖
https://nanchengjiumeng123.top/2022/05/27/framework/spring/Spring IoC/2022-05-27_Spring循环依赖/
作者
Yang Xin
发布于
2022年5月27日
许可协议