spring循环依赖过程解析

spring解决循环依赖

spring在创建和依赖注入单例对象时,通过三级缓存的设计,可以解决大多数的循环依赖问题,但是基于三级缓存的解决方案,对于部分场景还是无法解决循环依赖的问题,会由spring抛出异常。

spring的三级缓存由singletonObjectsearlySingletonObjectssingletonFactories组成:

  • singletonObjects:保存初始化完成的单例bean实例;
  • earlySingletonObjects:保存提前曝光的单例bean实例;
  • singletonFactories:保存单例bean的工厂函数对象;

对于通常的A依赖B、B依赖A的简单循环依赖的场景,spring的单例对象创建和依赖注入的流程如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class A {

@Autowired
private B b;

public A() {
}
}

@Service
public class B {

@Autowired
private A a;

public B() {
}
}

image

构造方法注入导致循环依赖

成功

对于相互依赖的Bean,如果全部都是构造方法依赖注入,spring必然无法解决这种循环依赖。但是如果如下场景,A通过@Autowired依赖B,而B通过构造方法依赖A,spring可以成功解决循环依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class A {

@Autowired
private B b;

public A() {
}
}

@Service
public class B {

private A a;

public B(A a) {
this.a = a;
}
}

image

循环依赖异常

但是对于这种部分Bean通过构造方法依赖注入的场景,spring的三级缓存并不是百试百灵。把上面成功依赖注入的A和B相互换位,A通过构造方法依赖B,而B通过@Autowired依赖A,则会出现spring无法解决循环依赖而抛出循环依赖的异常。

并没有修改依赖复杂度,仅仅是A与B相互换了依赖位置,spring就抛出了循环依赖异常,其中的流程如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class A {

private B b;

public A(B b) {
this.b = b;
}
}

@Service
public class B {

@Autowired
private A a;

public B() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
| a defined in file [D:\Program\workspace\other\springboot\target\classes\com\wakzz\autowire\A.class]
↑ ↓
| b (field private com.wakzz.autowire.A com.wakzz.autowire.B.a)
└─────┘

image

代理导致循环依赖

@Transactional代理成功

spring经常会对创建的单例对象进行代理,对于代理对象的依赖注入与非代理对象依赖注入有稍微的区别。例如通过@Transactional注解代理A后的依赖注入流程如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Transactional
@Service
public class A {

@Autowired
private B b;

public A() {
}
}

@Service
public class B {

@Autowired
private A a;

public B() {
}
}

image

@Async代理循环依赖异常

然而并不是每个代理对象的依赖注入流程都是一样的,典型的例子就是@Async注解,@Async经常会造成循环依赖的问题。将上面成功代理依赖注入的案例中的@Transactional注解换成@Async注解,应用启动后spring就会抛出BeanCurrentlyInCreationException异常,该流程如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Async
@Service
public class A {

@Autowired
private B b;

public A() {
}
}

@Service
public class B {

@Autowired
private A a;

public B() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:330) ~[spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:137) [spring-boot-2.0.4.RELEASE.jar:2.0.4.RELEASE]
at com.wakzz.Application.main(Application.java:20) [classes/:na]

image

有图可见,@Async@Transactional在对象创建和依赖注入的流程中的区别,仅仅是Bean对象被代理成代理对象的时机。@Transactional的代理是当从单例工程获取对象时,由单例工厂完成单例对象的代理;而@Async的代理则是在Bean对象依赖注入完成之后,再初始化并代理成代理对象。

@Async代理成功

跟部分对象构造方法注入的场景一样,不需要修改依赖复杂度,仅仅是修改了A与B的依赖关系,让被代理的对象后一步创建,就可以解决该依赖关系成功启动应用。spring的流程如下图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class A {

@Autowired
private B b;

public A() {
}
}

@Async
@Service
public class B {

@Autowired
private A a;

public B() {
}
}

image