循环依赖在 Spring 里什么时候能解决、什么时候不能?三级缓存到底给了谁一个半成品?为什么 Boot 默认把它关掉?循环加上代理会发生什么?这篇在 Spring Framework 7.0.9 上把六种情形逐个跑出来。
1. 构造 A
2. 构造 B
3. B.setA(拿到的 A 还没初始化完成)
4. B.@PostConstruct(此时 A.ready=false)
5. A.setB(B 已经构造且初始化完成)
6. A.@PostConstruct
a.b == b ? true;b.a == a ? true能解开,代价是 B 初始化时看到的 A 还没有初始化完成。
ctor → BeanCurrentlyInCreationException: Error creating bean with name 'ca': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
没有半成品可拿,直接抛 BeanCurrentlyInCreationException。
allowCircularReferences=false:BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
allowCircularReferences=true:启动成功SpringApplication 默认禁止循环引用,需要显式打开。
1. 构造 SA
2. 构造 SB
3. SB 注入 sa:原始对象
4. SA 注入 SB(此时 SB 里的 sa=SA)
5. 构造 SB
6. 构造 SA
7. SA 注入 SB(此时 SB 里的 sa=null)
8. SB 注入 sa:代理
isAllowRawInjectionDespiteWrapping=false;构造 SA 的次数 2,构造 SB 的次数 2
启动成功。容器里的 sa 是代理?true;SB 里持有的 sa 是代理?true;两者是同一个对象?true自定义 BPP 包装时 7.0.9 会把相关 Bean 重建一轮;Spring AOP 的自动代理创建器能提前暴露代理。
容器里的 PA 是代理?true;PB 持有的 pa 是代理?true;pb.pa.hello() = [切面]PA.hello;pb.pa 就是容器里的 PA?true
切面在循环依赖里依然生效。
1. 构造 LA(拿到的 LB 是一个懒加载代理)
2. 构造 LB构造器参数标 @Lazy 注入代理,可以绕过构造器循环。