惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
罗磊的独立博客
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
A
About on SuperTechFans
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog RSS Feed
IT之家
IT之家
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
宝玉的分享
宝玉的分享
C
Check Point Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
爱范儿
爱范儿
Microsoft Security Blog
Microsoft Security Blog
月光博客
月光博客
T
Tailwind CSS Blog
The Cloudflare Blog
Hugging Face - Blog
Hugging Face - Blog

祈雨的笔记

安全多方计算MPC spark原理解析 kueue执行源码分析 spark on k8s执行源码分析 spark-operator源码解析 系统压测遇到的缓存击穿问题 我的世界PC与安卓联机 蚂蚁金服流量投放平台的AIG改造 G1大对象致Old区占用率高 日志打印导致接口响应率下跌分析 Groovy加载类导致OOM分析 ERROR日志打印导致CPU满载 记OceanBase死锁超时 应用发版期间服务响应超时 Ark Serverless初探 系统优化复盘一二三 The user specified as a definer does not exist Kong网关初探 API网关选型调研 CPU火焰图常用工具 配置中心选型调研 root操作Nginx导致用户组错误 基于Proxifier使用代理 FastJSON字段智能匹配踩坑 Nacos初探 记一次Nginx服务器CPU满荷载故障 基于券系统分库分表的思考 limit不参与SQL成本计算致索引失效 Linux常用性能监控命令 golang低版本http2偶现400
spring循环依赖过程解析
祈雨的笔记 · 2019-11-02 · via 祈雨的笔记

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