(还写了一篇,内容和这个差不多的,emm....那篇更简洁,算是半伪码形式....https://blog.csdn.net/qq_36951116/article/details/100078947)
其实createBean方法没做什么事。主要就是:
(1)调用resolveBeforeInstantiation方法,作用是
在调用doCreateBean使用spring自身创建bean的流程之前,即在创建bean之前,执行了InstantiationAwareBeanPostProcessor类型的后置处理器,这种后置处理器可以用来自己创建bean的,并且这个自己创建的bean不用走后面doCreateBean这个bean创建流程。如果存在这种后置处理器,那么会由这种后置处理器去创建bean,如果这种后置处理器返回了自身创建的bean,即返回非null的话,createBean就不会再去调用doCreateBean了,而是直接把这个bean实例给返回。
(AOP的实现就是实现了这种后置处理器)
总之,通过实现这种后置处理器,就可以把自己创建的bean直接当做单例bean了(像填充属性初始化什么的,那都要自己做了)
(2)如果没有InstantiationAwareBeanPostProcessor类型的后置处理器,或者这种后置处理器没有自己创建bean,则createBean方法会调用doCreateBean去执行bean的创建流程。
doCreateBean执行流程大致
以A和B相互循环依赖为例子:
<bean id="a" class="com.mine.bean.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.mine.bean.B">
<property name="a" ref="a"/>
</bean>
(1)A在创建过程中,先通过singletonFactories把实例暴露出去(执行addSingletonFactory)
(2)接下来执行populateBean方法填充属性时A需要注入B的实例,
(3)调用getBean获取B实例,但在getBean-》doGetBean-》getSingleton中没有从缓存中获取到bean,意味着B的实例还没有创建,在接下来就要创建B了。
(4)创建B时,B这个时候也得执行populateBean方法,执行过程中发现B需要注入A的实例(循环依赖了),
(5)调用容器的getBean获取A的实例。
(6)在getBean-》doGetBean-》getSingleton中通过singletonFactories获取到了第(1)步中A早期暴露出来的A实例。此时A还是没有创建完成的,A还是停在上面的第(2)步的创建过程,所以通过getSingleton获取A的实例是获取到提前暴露的A。
(7)在getSingleton中从singletonFactories获取到了提前暴露的A之前,不仅要把A从singletonFactories移除,还要把A放入earlySingletonObjects中,这表示A的早期暴露实例已经被其他人获取了,已经被其他人拿去使用了(这里是被B拿去注入了)。
(8)然后doGetBean返回A,方法回溯到第(5)步,然后再回到第(4)步,成功获取到A,此时可以把A注入到B了。
(注意,这个时候B持有了A的早期暴露的实例,earlySingletonObjects的作用就是记录A早期暴露的实例已经被别人获取了)
(9)第(4)步的doGetBean执行结束,返回B。回溯到(2),A注入了B。(B不一定就是早期暴露的那个,也可能是被后置处理器处理后的代理,B的实例是早期暴露的还是被代理了的不影响,可以略过括号这段话)
(10)第(2)步的populateBean执行完,也就是A的属性填充玩了,接下来的下一行代码是:
exposedObject = initializeBean(beanName, exposedObject, mbd);
initializeBean内部会执行后置处理器,这里面的后置处理器是用户自定义的,可能是返回一个替换bean,也可能是返回一个代理的bean,那看写这个后置处理器的人想干嘛了。
(11)接下来,判断initializeBean是否返回了一个新的bean (用A+用于和之前早期暴露的A区分开):
如果不相等,那就需要判断A是否被其他bean注入了,如果A已经被其他bean注入了,并且initializeBean返回的是A+的话,
由于A!=A+,spring就会抛异常。
这么做就是为了保证A+和A不会同时出现在应用中,为了保证A这个类的bean是单例的。否则会出现早期暴露的A被注入到了其他bean中,A+却是被保留在spring容器中。同时存在A和A+,这样就不是单例的了。
//这里beanName是"a"
//exposedObject为后置处理器返回的A的实例A+(不清楚有没有被替换或者代理,后面进行判断)
exposedObject = initializeBean(beanName, exposedObject, mbd);
//省略代码.......
//getSingleton("a", false) 表示获取被其他bean获取过的原始的A实例
Object earlySingletonReference = getSingleton(beanName, false);
//如果A的原始实例没有被其他bean获取过,那就什么事也没有了,直接返回A
//如果被获取过,那就继续
if (earlySingletonReference != null) {
//判断initializeBean是否产生了A+,而不是原来的A
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
//如果initializeBean产生了A+,然后A已经被其他bean依赖了
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//获取依赖了A的bean,这些bean有些创建完成了,有些还没创建完。
String[] dependentBeans = getDependentBeans(beanName);
//已经完全创建完成,并且依赖了A的bean集合
//(而正在创建中的,虽然也注入了原始的"a",但在创建过程中还可以改成新的"a")
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
//添加已经创建完成的且依赖A的bean(属性注入,后置处理都搞完了的那种bean)
for (String dependentBean : dependentBeans) {
//如果bean是否已经创建完成了,就添加到集合中
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
//如果存在已经创建完成的bean注入了A,spring无法再操作创建完成的bean了,
//此时必须抛异常,否则无法保证A的实例是单例的,会导致A和A+同时存在。
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] 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.");
}
}
}
上面抛异常的原因是因为循环依赖导致早期暴露的bean被出入+后置处理器产生了新的实例 共同产生的。spring 直接给你抛异常了。。所以碰到这种问题的时候,要么去除循环依赖,要么,去除或者修改后置处理器。。