spring的循环依赖可以暴露出很多关键的问题。可以纠正市面上几个理解的不对的地方:
- AOP执行时间并不都是在spring执行完生命周期回调执行,当发生循环依赖的时候在提前暴露的工厂getObject的时候进入getEarlyBeanReference(beanName, mbd, bean)已经提前完成AOP了
这篇文章适合已经读过spring源码的读者看,因为很多流程我这里不会提到,当然后续会继续更新spring的整个流程。话不多说,开始咯!!
应用代码
Appconfig.java类的代码
@Configuration
@ComponentScan("org.debug")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig {
// @Bean
// public Ca createCa(){
// System.out.println("init ca");
// return new Ca();
// }
//
// @Bean
// public Cb createCb(){
// System.out.println("init cb");
// createCa();
// return new Cb();
// }
}
A.java类的代码
@Component
public class A {
@Autowired
private C c;
public A(){
System.out.println("A被创建 了");
}
public void executor(){
System.out.println("A executor");
}
}
C.java类的代码
@Component
//@Scope("prototype")
public class C {
@Autowired
private A a;
public C() {
System.out.println("C呗创建");
}
public void executor(){
System.out.println("C executor");
}
}
这两个类非常简单,就是相互引用了对方,也就是我们常常的说的循环依赖,spring是允许这样的循环依赖(前提是单例的情况下的,非构造方法注入的情况下,还有开启循环依赖的情况,当然spring默认是开启的)
运行结果图
读者只要看上面圈起来的红色的就可以了,已经可以证明spring是支持循环依赖的。
下图是我修改spring源码让它不支持循环依赖
那么为什么setAllowCircularReferences(false);会关闭循环依赖呢?首要明白spring的循环依赖是怎么做到的呢?spring源码当中是如何处理循环依赖的? 分析一下所谓的循环依赖其实无非就是属性注入,或者就是大家常常说的自动注入, 故而搞明白循环依赖就需要去研究spring自动注入的源码;spring的属性注入属于spring bean的生命周期一部分;怎么理解spring bean的生命周期呢?后面我会继续说,不会在本文中说明。
要理解bean的生命周期首先记住两个概念
请读者一定记住两个概念——spring bean和对象:
- spring bean——受spring容器管理的对象,可能经过了完整的spring bean生命周期(为什么是可能?难道还有bean是没有经过bean生命周期的?答案是有的,具体我们后面文章分析),最终存在spring容器当中;一个bean一定是个对象
- 对象——任何符合java语法规则实例化出来的对象,但是一个对象并不一定是spring bean;
所谓的bean的生命周期就是磁盘上的类通过spring扫描,然后实例化,跟着初始化,继而放到容器当中的过程
上图就是spring容器初始化bean的大概过程(至于详细的过程,后面文章再来介绍);
文字总结一下:
1:实例化一个ApplicationContext的对象;
2:调用bean工厂后置处理器完成扫描;
3:循环解析扫描出来的类信息;
4:实例化一个BeanDefinition对象来存储解析出来的信息;
5:把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化bean;
6:再次调用bean工厂后置处理器;
7:当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等;
8:如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
9:推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;
10:spring处理合并后的beanDefinition
11:判断是否支持循环依赖,如果支持则提前把一个工厂存入singletonFactories——map;
12:判断是否需要完成属性注入
13:如果需要完成属性注入,则开始注入属性
14:判断bean的类型回调Aware接口
15:调用生命周期回调方法
16:如果需要代理则完成代理
17:put到单例池——bean完成——存在spring容器当中
大概了解下整个过程,后面文章会详细分析,当前先进行循环依赖的分析。
源码分析
流程图
先看下循环依赖流程图,大概有个映象:
找到解决循环依赖源码所在
- refresh方法当中调用finishBeanFactoryInitialization方法来实例化所有扫描出来的类
- preInstantiateSingletons方法经过一系列判断之后会调用getBean方法去实例化扫描出来的类
- getBean方法就是个空壳方法,调用了doGetBean方法
doGetBean方法内容有点多,这个方法非常重要,不仅仅针对循环依赖,
甚至整个spring bean生命周期中这个方法也有着举足轻重的地位,当然本文会对它解决循环依赖部分进行分析。需要说明的是我为了更好的说清楚这个方法,下面重点说这个doGetBean方法。
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//transformedBeanName(name)这里的name就是bean的名字,先这样理解 当然实际上不仅如此
final String beanName = transformedBeanName(name);
//定义了一个对象,用来存将来返回出来的bean
Object bean;
//现在我们循环依赖的场景是A类注入C,发现C类不在容器,则去创建C,创建完C要注入A属性,发现A不在容器,去创建A,可以整体分为几部。
//1.实例化A对象 2.往A对象注入C属性 3.创建C对象 4.往C对象注入A属性 5.A此时不在单例池所以去创建A,但是这时候
//调用getSingleton(beanName, true)判断的时候已经包含在这个正在创建的单例池this.singletonsCurrentlyInCreation.contains(beanName)。
//而且allowEarlyReference又是允许为true,这个时候它根据beanName去拿到提前暴露的工程,调用getObject,则这时候如果有代理的
//话,就会去执行代理,这样才是一个完整的A,假如一开始没提前暴露工厂,而是只是一个不完整的对象,最终注入也是不完整的。
//最后把这个完整的bean存储,this.earlySingletonObjects.put(beanName, singletonObject);
//然后返回这个完成代理的bean(如果没有AOP,则也不会是个代理bean),返回bean注入到C。
//C注入完毕开始执行生命周期回调和最后一个beanPostProcessor完成AOP设置
//然后返回Cbean,A注入C,完成循环依赖。
//这边getSingleton第一次调用的时候啥时候都不做,然后进入下面的sharedInstance = getSingleton(beanName, () -> {,
//会做一件重要的事,this.singletonsCurrentlyInCreation.add(beanName)。然后用addSingletonFactory提前暴露工厂this.singletonFactories.put(beanName, singletonFactory);
//这样第二次再调getSingleton(beanName, true)才有了获取提前暴露的工厂,调用getObject。
Object sharedInstance = getSingleton(beanName);
/**
* 如果sharedInstance不等于空直接返回
* 当然这里没有直接返回而是调用了getObjectForBeanInstance
* 关于这方法以后解释,读者可以认为这里可以理解为
* bean =sharedInstance; 然后方法最下面会返回bean
* 什么时候不等于空?
* 再容器初始化完成之后
* 程序员直接调用getbean的时候不等于空
* 什么时候等于空?
* 上文已经解释过了,创建对象的时候调用就会等于空
*/
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}else{
/**
* 需要说明的笔者删了很多和本文无用的代码
* 意思就是源码中执行到这个if的时候有很多其他代码
* 但是都是一些判断,很本文需要讨论的问题关联不大
* 这个if就是判断当前需要实例化的类是不是单例的
* spring默认都是单例的,故而一般都成立的
* 接下来便是调用第二次 getSingleton
* 第二次会把当前正在创建的类记录到set集合
* 然后反射创建这个实例,并且走完生命周期
**/
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
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);
}
return (T) bean;
}
提前暴露工厂,在实例化好对象,还没注入属性的时候添加的
//循环依赖的条件必须bean是单例支持循环依赖,并且这个bean当前正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//解决循环依赖,addSingletonFactory会提前暴露bean工厂
//而() -> getEarlyBeanReference(beanName, mbd, bean)需要这个暴露的bean工厂去getObject
//如果你开启@EnableAspectJAutoProxy则这里会多了一个后置处理器,AnnotationAwareAspectJAutoProxyCreator
//处理代理逻辑
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)) {
//提前暴露一个bean工厂,为后面解决循环依赖,这个工程是jdk为调用者生成的代理,
//假如从AbstractAutowireCapableBeanFactory调过来,则singletonFactory是AbstractAutowireCapableBeanFactory$lambda@1583
//那这个真正有意义的时候是调用singletonFactory.getObject的时候,就会去触发() -> getEarlyBeanReference(beanName, mbd, bean)
//而() -> getEarlyBeanReference(beanName, mbd, bean)这段会去触发后置处理器完成各种事情
//所以这里只是埋下伏笔
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
这个方法主要是循环依赖的时候提前去完成AOP代理会调用
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//开天辟地六个后置处理器
//ApplicationContextAwareProcessor
//ConfigurationClassPostProcessor
//PostProcessorRegistrationDelegate
//CommonAnnotationBeanPostProcessor
//AutowireAnnotationBeanPostProcessor
//ApplicationListenerDetector
//如果开启了AOP,则会多了个AnnotationAwareAspectJAutoProxyCreator,其实这里最重要是这个,这里就是要完成AOP代理的
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
大家喜欢的点个赞,关注下我,后续会继续更新spring的完整源码分析哦!!!!!!