四、spring源码循环依赖的处理之doCreateBean方法的执行流程(文字描述)

(还写了一篇,内容和这个差不多的,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 直接给你抛异常了。。所以碰到这种问题的时候,要么去除循环依赖,要么,去除或者修改后置处理器。。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值