- 在讲解之前我们先明确下本文的核心问题,什么是循环依赖?
循环依赖的产生是两个或多个对象在相互使用时形成了闭环依赖,比如最简单的例子A 类依赖 B,B类又依赖A.单独理解其实并没有问题,那么为什么spring会产生问题呢?答案很简单那便是IOC,如果是人为的处理 相互new便是了,但是IOC 没有那么聪明他只知道遇到需要注入的值就去设置,这样就形成了无限制依赖永远循环设置下去.
- 那么spring是如何解决的?
按照上方的逻辑设计会产生循环依赖,因为他创建的同时立马进行了属性的初始化,那么怎么解决?将一个类型的创建分步骤进行便可,区分为实例化和初始化两个操作,然后再将实例化的对象暴露出去提供给后续依赖创建人使用.这里可能有疑问为什么暴露就可以解决?这就要提到spring在初始化对象的时候并不是所有对象创建完成后才初始化,他是在创建对象的过程中进行初始化的,比如A依赖了B,A创建完后进行初始化的时候发现了需要B,便去获取如果获取不到则创建B,而初始化B的时候发现需要A又去获取此处便能获取到A(如果获取不到那就不是将初始化和实例化分开了),因为A在创建完的第一个步骤就是将其设置到缓存中方便后续使用,从而完成类的创建.
- 难道一个缓存map就能解决?
如果你不嫌弃麻烦是可以用一个map即存储实例化也行也存储初始化完成的对象,但是这样会增加你开发难度,因为有的时候你可能就需要一个完整的初始化对象而不是一个未初始化仅实例化的对象.
- 那我两个缓存map能解决?
这里能保证的说一定可以,但是..spring有两个核心IOC/AOP,如果你只想解决IOC的注入依赖问题那么两个map完全能解决问题,但是一旦参合了AOP就不一定了.
- AOP 最终做了什么?
AOP最终是给你的目标类生成一个代理子类出来.
- 只是生成代理子类为什么会影响到IOC呢?
因为IOC会产生一个问题,注入类应该注入的是代理类还是原始类?如果是原始类那么就不存在AOP,要是代理类那么我们之前IOC只是实例化了原始类并没有创建代理子类啊.
- 那这就涉及到一个问题怎么将原始类替换为代理类?
如果是想替换就有点走歪路了,我们能不能创建第三个缓存用于回调创建代理,谁要依赖我那么你用我在三级缓存里设置的回调方法获取我的代理类,实际是在回调方法里创建我的代理类并且进行返回.
说到这里我们便推演除了三级缓存为什么存在,接下来用实际代码逐行解释spring的实现.
//AbstractAutowireCapableBeanFactory.createBean:520 行
//这里是个神奇的操作,此处可以扩展自定义创建bean,如果有自定义bean的创建则直接返回结果,不在处理后续
//如果没有则继续操作(AOP在此方法内也做了实现,用于创建当前对象代理类使用的是)
//resolveBeforeInstantiation.applyBeanPostProcessorsAfterInitialization方法(此处是层级关系)
//此处捎带一嘴不参合三级缓存
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
//同一类下面的才是操作三级缓存的方法
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//此代码是上方方法内部的解决循环依赖的核心调用
//此处查看是否需要解决循环依赖并且将类暴露出去 : AbstractAutowireCapableBeanFactory.createBean:595行
//mbd.isSingleton() 当前对象是否为单例(不是单例就不会产生循环依赖了,让他们创建就是了)
// this.allowCircularReferences spring的配置 是否需要解决循环依赖问题
//isSingletonCurrentlyInCreation 当前对象是否正在创建(在获取getBean的时候就将此类添加到了一个set中)
//此处是判断是存在于set中,如果存在则说明需要防止发生循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
//只有上方结果为true才会进去
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//还记得最后一个问题的答案吗?第三级缓存用于存储对当前类加强的回调方法,此处便是将一个表达式
//设置到三级缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//addSingletonFactory 便是三级缓存.put(beanname,getEarlyBeanxxx)
}
//填充属性,发现没和上方的描述一样 前面实例化了bean 然后将其丢到三级缓存中 然后在进行初始化
//为什么先丢到缓存中在初始化呢?在`那么spring是如何解决的?`问题中我有解释spring创建是依赖创建
//而不是整体创建完再解决依赖问题,此populateBean方法便是解决依赖的处理,内部处理property属性值
//从BeanFactory中resolveDependency,在其中最终使用了getBean方法.从而又绕回来了
//因为此处就是从getBean过来的,从而达成了超远距离的递归,用来解决依赖注入的问题
//但是同时也引入了循环依赖的问题,那么但是因为上方add到缓存中后所依赖的类在使用populateBean
//便可以从缓存中获取到所需要的依赖bean,从而解决了循环依赖(死循环创建)的问题.
//但是此处还没有引入上方提到的AOP代理的问题,再想想如果递归解决依赖的问题是不是导致最最开始的对象
//一直卡在当前populateBean 就没办法完成,但是呢依赖对象又需要当前类的代理类,那么如何处理?
populateBean(beanName, mbd, instanceWrapper);
//既然有了这个问题那么接下来继续
//970行的代码,此处是上方addSingletonFactory所传入的方法
//笔者与读者此处必须要统一一个思想那便是 能调用此方法的地方一定是循环依赖了需要的代理类
//如(A依赖B,B依赖A,先创建A)所以此处一定是B在获取A依赖时调用的
//如简图:(getBean) A ->(getBean) B -> (getEarlyBeanReference)A 此处描述了获取对象用到的方法
//既然统一了思想那么我们继续
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
//mbd.isSynthetic() 当前已经是代理对象则不用再处理
//hasInstantiationAwareBeanPostProcessors() 判断是否有代理的创建器
//smartInstantiationAware说白了就是判断他是否为空,必然不会为空不然此处代理对象将不会被创建
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
//此方法spring 中只有一处实现
//AbstractAutoProxyCreator.getEarlyBeanReference 237行
//此处不展开只简单理解为此处为当前exposedObject创建了一个代理对象并且返回
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
//此处返回创建的代理对象
return exposedObject;
}
//到此处我们B对象所依赖的代理对象A已经创建完成并且依赖完成了,然后一步步返回再次到了populateBean
//此处我们需要再次统一思想,到此处的当前对象是A 而不是代理A 并且当前A已经依赖了代理B并且B依赖了
//代理A,如图: A->B(代理)->A代理->B代理...无限循环
//既然统一了那么我们继续
populateBean(beanName, mbd, instanceWrapper);
//到此处我们再次抛出一个问题,如果是非循环依赖的代理对象没人调用getEarlyBeanReference那么由谁创建?
//便是他的下一行进行的处理,此方法内有一个重要得代理处理applyBeanPostProcessorsAfterInitialization
//此方法内有个AbstractAutoProxyCreator.postProcessAfterInitialization用来进行代理对象的处理
//因为上方的循环依赖以及解决了当前A的代理对象所以此方法内会跳过对当前A的处理防止结果不再是单例
//所以如果是循环依赖此处只做扩展的处理,如果不是则此处会创建出代理对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
//好了 到此处我们再次统一下思想,当前的exposedObject 还是A而不是代理对象A,因为上面解释过
//此方法如果是循环依赖除非当前用户做了特殊的操作扩展否则不会再进行代理对象的创建
//那么我们现在已经有两个对象了第一个 当前的原始A对象,此处正在创建他,第二个代理对象A,在我们的二级缓存中
//三级缓存的触发条件只有在获取对象的时候一二级都不存在,当触发三级缓存的时候会从上方的getEarlyBeanReference中
//获取并且创建代理对象,然后将三级缓存移除,将获取的结果存储到二级缓存中,为了方便理解下面是获取代码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从一级缓存中获取完整对象
Object singletonObject = this.singletonObjects.get(beanName);
///不存在并且正在创建则从二级中获取
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//二级还不存在并且需要解决循环依赖则触发三级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//如果是多线程环境下可能上面获取的方式不是原子的所以此处上锁在此重复一遍
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//最终从三级缓存中获取getEarlyBeanReference的表达式
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//此getObject便是调用上方的表达式最终得到代理对象
singletonObject = singletonFactory.getObject();
//得到代理对象后将其丢入到二级缓存中
//因为三级缓存中的对象一定不是完整对象,还在populateBean卡着呢
this.earlySingletonObjects.put(beanName, singletonObject);
//然后移除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
//此处证实了上方的说明当前二级缓存中存在代理对象A当前initializeBean处存在的是原始A对象
//问题又来了该返回谁?因为经历过initializeBean方法当前的A一定是一个完整的对象(最终结果)
//但是因为引用类型的特定在A对象完整的同时代理A也就完整了,所以我们应该返回谁?
//当然是返回代理A因为依赖A对象的对象最终都引用的是A代理而不是当前A,好既然有了思想我们继续
//这是接下来的代码,有一次校验是否需要解决循环依赖,为什么?
//我们上面提的两个对象返回谁的问题只有循环依赖才会出现,所以此处需要解决他俩返回谁的问题
if (earlySingletonExposure) {
//从缓存中获取当前A的代理对象
Object earlySingletonReference = getSingleton(beanName, false);
//如果获取到说明循环依赖了,则不等于null
if (earlySingletonReference != null) {
//比较下他俩是否相等,需要注意他俩是谁?他俩并不是我们上面从缓存获取到的A代理
//而是原始创建的A对象bean和进过上方init方法处理的对象
//如果他俩相等则万事大吉因为内部没有重新创建,因为一旦重新创建变代表着
//出现了第三个A对象,那就是三个A里选谁的问题了,当然第三A的出现替换掉了第一个A
if (exposedObject == bean) {
//此处很暴力..A和代理A不用想一定要用创建好的代理A而不是当前对象A
exposedObject = earlySingletonReference;
}
//接下来就是用户创建的A和系统创建代理A的处理
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//首先获取所有的依赖对象,因为能到这里已经是循环依赖,所以此处需要获取
//来判别剩下的处理
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
//查看当前依赖中是不是存在已经创建完成的bean
//此方法很简单如果是未完成创建的bean则从三级缓存中移除依赖bean
//如果依赖bean已经创建完整则添加到集合中
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
//一旦存在已经完成创建的bean直接抛出异常,不在进行依赖解决
//因为没办法将当前用户创建的对象丢到循环依赖的对象中所以此处报错
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException();
}
}
}
}
//OK,到此处三级缓存的解释以及完成了,还有点尾巴..获取到的结果是什么时候添加到缓存中的呢?还有二级缓存移除?
//创建类的时候会调用DefaultSingletonBeanRegistry.getSingleton#231行,它里面调用了createBean
//调用的结果addSingleton(beanName, singletonObject);此处用来添加到一级缓存中并且移除二三级缓存
到此处文章结束,还有不了解的小伙伴可以下方留言