上一节分析过,就算发生循环依赖且提前AOP,最终也可以通过二级缓存把最终的代理对象放入二级缓存:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
exposedObject:初始化后得到的对象
bean:一开始实例化的原始对象
大部分情况下exposedObject == bean,什么情况下exposedObject != bean呢?
先看初始化后方法:
aop其实只是其中一个BeanPostProcessor的功能,并且它的实现是:
发生了循环依赖(提前AOP,二级缓存中有值),返回普通对象,此时exposedObject == bean
没有发生循环依赖(没有提前AOP,二级缓存中无值),则进行AOP返回代理对象
此时二级缓存中无值,且getSingleton方法的入参allowEarlyReference=false,返回是null
根本不会进入这段逻辑:
除开aop的BeanPostProcessor会处理传进来Bean之外,其他BeanPostProcessor也会处理,其他BeanPostProcessor有可能会直接替换传进来的Bean。
比如:
@ComponentScan(value = "com.yth.application")
@EnableAspectJAutoProxy
@EnableAsync//开启异步执行功能
public class AppConfig {}
@Component
public class AService {
@Autowired
private BService bService;
@Async//开启异步
public void test() {
System.out.println(bService);
}
}
@Component
public class BService {
@Autowired
private AService aService;
public void test() {
System.out.println(aService);
}
}
启动,抛异常了,就是刚刚讲的这段代码的else:
此时exposedObject != bean,因为初始化后的时候把exposedObject改了
按照之前分析,有循环依赖,所以AOP的实现中返回的还是原始对象
但是走到AsyncAnnotationBeanPostProcessor,要支持异步就会生成另外一个代理对象(代理对象中,通过单独开一个线程执行相应方法达到异步执行效果)。
所以导致exposedObject != bean
本质原因其实是
那为什么要抛异常呢?
之前的案例,发生循环依赖以后:
B注入的A,是二级缓存中的值,getEarlyBeanReference得到的对象,当前场景下就是aop的代理对象(AsyncAnnotationBeanPostProcessor没有提供这个功能)
而A经过初始化后,拿到的是AsyncAnnotationBeanPostProcessor生成的代理对象,如果不做处理,放入单例池的就是这个代理对象
放入单例池的和赋值给B的(二级缓存中的对象)不一样,所以要抛异常。
那怎么办呢?
加了@Lazy就不报错了,为啥?
因为之前出现一系列问题,是因为发了循环依赖,创建A的过程需要B,创建B的过程又需要A
但是加了@Lazy注解以后,创建A,填充属性B的时候,发现是@Lazy懒注入,则生成一个代理对象直接赋值了(懒注入原理见前面依赖注入章节),不会再去找B、创建B了,A直接能正常走完后续生命周期流程,最终放入单例池。
而到单例B创建的时候,填充属性A时直接能从单例池拿到A,B也能正常走完后续生命周期流程。
当A真正用到属性B,执行其方法的时候,才会去找真正的Bean,此时A早已经在单例池中了。
即使用 @Lazy 直接打破了循环依赖,根本不会发生循环依赖!!!!从根本解决了问题。
之前给A加了@Async注解,A本身是发生循环依赖的,所以会报错
而对于B本身来说,其实并没有发生循环依赖,所以@Async放到B上不会报错
如果把@Async从AService去掉,放到BService上:
@Component
public class AService {
@Autowired
private BService bService;
public void test() {
System.out.println(bService);
}
}
@Component
public class BService {
@Autowired
private AService aService;
@Async
public void test() {
System.out.println(aService);
}
}
不会报错为啥?
主要原因是顺序,AService是先创建的
先创建A,先给A填充属性B,发现B没有,会去创建B,B再去填充属性A
当B去填充A的时候,对于A来说,A发生了循环依赖,所以这里A提前AOP拿到代理对象给B注入
而B本身并没有发生循环依赖,后续会正常的走完流程,在步骤2.5中,先根据aop的到一个代理对象,在根据Async给AOP的代理对象再生成一个代理对象…最终放到单例池中,是OK的
B成为完全Bean给A赋值后,A继续走后续流程,由于A没有被@Async标记,所以初始化后不会生成新的代理对象(不会与AOP提前生成的代理对象冲突),返回的是原始Bean,按照之前分析最终会用二级缓存的值替换放入单例池中,最终A、B放入单例池的,与他们相互引用的都是同一个对象,正常的。
其实本质原因是AsyncAnnotationBeanPostProcessor不支持循环依赖的情况,所以直接从根本解决。
改造AsyncAnnotationBeanPostProcessor,让他实现SmartInstantiationAwareBeanPostProcessor接口,实现getEarlyBeanReference方法,支持提前生成代理对象,并重写postProcessAfterInitialization方法,模仿AOP的实现,没有发生循环依赖,再生成代理对象,否则返回原来的Bean,即原始对象。(注意AsyncAnnotationBeanPostProcessor的顺序应该是放在AOP实现之后的)
为什么可行?感兴趣参考:参考链接
A、B都是原型情况,其实解决不了(也不是解决不了,可能没什么意义或者应用场景):
一个是原型另一个是单例没关系:
源码分析
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
这里会检查多例Bean是不是已经在创建中了,如果是,说明发生了循环依赖,会抛异常
org.springframework.beans.factory.support.AbstractBeanFactory#isPrototypeCurrentlyInCreation
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
//返回指定的原型bean当前是否正在创建中(在当前线程内)。
//prototypesCurrentlyInCreation是ThreadLocal
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
使用@Lazy懒注入其实可以解决,启动的时候不会报错,但每次调用代理对象方法的时候,都会重新创建一个Bean,可能没什么意义。
@Component
public class AService {
public AService(BService bService) {
this.bService = bService;
}
private BService bService;
public void test() {
System.out.println(bService);
}
}
@Component
public class BService {
public BService(AService aService) {
this.aService = aService;
}
private AService aService;
public void test() {
System.out.println(aService);
}
}
构造方法中A依赖B,B依赖A
直接报错,因为此时连对象都实例化不了,半成品都生成不了
实例化A 需要B -> 实例化B 需要A -> …
怎么解决?用@Lazy!!!打破循环依赖!!
之前@Async给A会报错,换成@Transaction呢?
@ComponentScan(value = "com.yth.application")
@EnableAspectJAutoProxy
@EnableTransactionManagement//开启事务管理
public class AppConfig {}
不会报错,具体不演示了。
因为@EnableTransactionManagement注解,并没有向容器里添加BeanPostProcessor,而是Advisor,通过AOP实现的事务管理(AOP的通知,AOP原理以后讲)
@Component
public class AService {
@Autowired
private AService aService;
}
创建A -> 注入A -> 单例池没有 -> 发现循环依赖 -> 二级缓存也没有 -> 利用三级缓存提前获取引用,存入二级缓存,并返回 -> 赋值给属性A -> A继续走后续生命周期流程…一样的
至此,循环依赖告一段落,总结一下三级缓存:
为什么需要singletonFactories?假设没有singletonFactories,只有earlySingletonObjects,earlySingletonObjects是二级缓存,它内部存储的是未经过完整生命周期的bean对象,Spring原有的流程是出现了循环依赖的情况下:
那如果没有singletonFactories,该如何把原始对象或AOP之后的代理对象放入earlySingletonObjects中呢?何时放入呢?
首先,将原始对象或AOP之后的代理对象放入earlySingletonObjects中的有两种:
所以,我们可以发现,现在Spring所用的singletonFactories,为了调和不同的情况,在singletonFactories中存的是lambda表达式,这样的话,只有在出现了循环依赖的情况,才会执行lambda表达式,才会进行AOP,也就说只有在出现了循环依赖的情况下才会打破Bean生命周期的设计,如果一个Bean没有出现循环依赖,那么它还是遵守了Bean的生命周期的设计的。