spring实例化bean之循环依赖

news/2024/11/8 23:07:29/

serviceA里注入了serviceB,serviceB里又注入了serviceA,这样在实例化serviceA时,在doCreateBean时的populateBean时会先实例化serviceB,然后实例化serviceB,在serviceB的doCreateBean方法的populateBean又会去找servcieA,这样循环依赖就产生了。
解决办法就是在populateBean之前把当前的serviceA提前暴露,然后在实例化B时可以找到这个提前暴露的serviceA。
关键方法是doCreateBean方法中的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

1. DefaultSingletonBeanRegistry#getSingleton

这里就是对当前bean做了一个标志,在实例化前表明当前bean在创建中,在实例化后删除这个标志

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}//关键1 标志A这个Bean在创建中beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<>();}try {//调用createBean方法singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}//关键2 //this.singletonsCurrentlyInCreation.remove(beanName)//删除掉Bean A创建中的标识afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}}return singletonObject;}}

2. AbstractAutowireCapableBeanFactory#doCreateBean

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = instanceWrapper.getWrappedInstance();Class<?> beanType = instanceWrapper.getWrappedClass();if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.//这里如果是单例 并且允许循环依赖 并且是正在创建的bean(1中设置)//能满足这个三个条件,则Spring会认为这个此时A是需要提前暴漏的单例Beanboolean 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(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}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.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}

3. DefaultSingletonBeanRegistry#addSingletonFactory

实例化A时,doCreateBean方法populateBean方法执行之前,也就是实例化注入的serviceB之前会调用这个方法。
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 也就是serviceA关联一个singletonFactory(也就是那个lambda表达式)this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}

给A关联了一个ObjectFactory,也就是getEarlyBeanReference对应的lambda函数对象。放入了singletonFactories中,当B去查BeanA的时候就可以通过getEarlyBeanReference方法获取exposedObject这个早期bean对象。

4.AbstractAutowireCapableBeanFactory#getEarlyBeanReference

这里基本就是把传入的bean返回。命名为exposedObject 早期暴露对象
调用逻辑就是 serviceB实例化时需要注入serviceA,然后在doGetBean的最开始调用getSingleton方法时会获取到serviceA的ObjectFactory,然后执行singletonFactory.getObject()就会执行这个方法拿到bean。

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;}

查找
比如在实例化serviceA时需要实例化serviceB,实例化serviceB时populateBean方法会再尝试实例化serviceA,doGetBean方法开始会调用getSingleton方法,这个方法里会找到提前暴露的serviceA,保证serviceB可以顺利创建。

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {//这里获取不到 因为servcieA的实例化还没完成 也就是servcieA的createBean方法还没执行完,servcieA的getSingleton方法的//singletonObject = singletonFactory.getObject()方法也就没执行完,下面的addSingleton方法//就不会执行(在这里执行this.singletonObjects.put(beanName, singletonObject)),	//singletonObjects中就不会有serviceA。Object singletonObject = this.singletonObjects.get(beanName);//判断会通过 第二个判断条件是在getSingleton方法的beforeSingletonCreation(beanName)设置的 最开始实例化A时会调用if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {//这里也获取不到,因为在最开始servcieA实例化时的addSingletonFactory方法会执行//this.earlySingletonObjects.remove(beanName);singletonObject = this.earlySingletonObjects.get(beanName);//判断会进入if (singletonObject == null && allowEarlyReference) {//这里能获取到,因为在servcieA实例化时的addSingletonFactory方法会执行//this.singletonFactories.put(beanName, singletonFactory);ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {//执行这个方法会执行那个lambda表达式 () -> getEarlyBeanReference(beanName, mbd, bean)//获取到serviceAsingletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}//获取到返回 此时serviceB就可以得到serviceA了,然后继续创建serviceBreturn singletonObject;}

总结
实例化过程:
1.finishBeanFactoryInitialization(beanFactory);
2.beanFactory.preInstantiateSingletons();
3.getBean(beanName);
4.doGetBean
在这里插入图片描述

这里实例化serviceA时找不到,但是在实例化注入的serviceB时又需要实例化serviceB注入的serviceA,这时就可以获取到
serviceA了。

5.
在这里插入图片描述
这里的getSingleton方法在执行时执行到**singletonObject = singletonFactory.getObject()**时就会执行lambda表达式的createBean方法。
依赖关系的处理是在createBean方法的doCreateBean方法的populateBean方法
6.doCreateBean
在这里插入图片描述
关键方法

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这个方法在实例化serviceA,populateBean之前,把serviceA及关联的ObjectFactory(lambda表达式)加到了singletonFactories,这样serviceB实例化时需要实例化servcieA,在doGetBean一开始的getSingleton方法会获取到serviceA,这样就能保证serviceB能创建完成,然后再创建serviceA。

在这里插入图片描述


http://www.ppmy.cn/news/242796.html

相关文章

C++ new和delete的使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、new和delete介绍二、简单使用1.new和delete2.自定义对象3.new[]和delete[]4.主存耗尽5.try&catch6.nothrow7.看下源代码 前言 new和delete是C里非常重…

Revit干货|自动捕捉遇到困难?这份秘诀请收好!

在BIM行业里&#xff0c;Revit往往影响着我们的建模效率&#xff0c;尽管软件提供了许多功能&#xff0c;但在建模过程中还是会因繁琐的操作而浪费很多时间。 因此&#xff0c;在使用Revit建模时&#xff0c;我们需要掌握一些小技巧来提升效率&#xff0c;如快捷键的使用和工具…

RFID门禁系统

RFID门禁系统 前言一、RFID射频二、功能演示三、我们使用的硬件和软件三、使用步骤1.新建工程2.程序 总结 前言 这一个RFID项目主要是通过RC522模块来达到智能识别ID卡&#xff08;RFID卡&#xff09;&#xff0c;通过识别ID卡号从而实现门禁的功能。 当然&#xff0c;这个项目…

新一代智能门禁解决方案

1 业界背景 我国的安防行业从上世纪起步&#xff0c;到今天已经全面开花。 安防从业者们靠着自己的勤劳和智慧&#xff0c;在这块合适的土地深耕&#xff0c;从一开始的跟随者&#xff0c;一步一步上升到了引领者。 近些年来&#xff0c;芯片的算力越来越高&#xff0c;各种优…

IC门禁系统

前述&#xff1a; QQ&#xff1a;961209458 V X&#xff1a;F9986858 承接毕业设计。 1.概述 &#xff08;1&#xff09;本设计采用STC89C52作为主控芯片&#xff0c;专用读卡器模块用来读射频卡的信息&#xff0c;当有卡进入到读卡器读卡的范围内时就会读取到相应的卡序列号…

基于树莓派的门禁系统

这几天黑客松比赛&#xff0c; 在大神的带领下稳稳地落幕了&#xff0c; 虽然是第二次参加这个活动了&#xff0c; 但是这一次才是我尽全力的去做自己的东西&#xff0c; 这里也感谢xx大牛的指导 。 也不废话了&#xff0c; 直接上图吧 大致的一个流程就是以树莓派作为中间人&…

#智能宿舍门禁 esp8266+RC522模块+DFPlay Mini Mp3模块实现宿舍智能门锁//NFC//物联网

esp8266RC522模块DFPlay Mini Mp3模块实现宿舍智能门锁//NFC//物联网 提示&#xff1a; 提示&#xff1a; 文章目录 esp8266RC522模块DFPlay Mini Mp3模块实现宿舍智能门锁//NFC//物联网前言一、实现的具体功能二、硬件材料1、材料清单2、线路连接 三、软件代码1、arduino安装…

小区安防门禁系统

&#xff08;一&#xff09;技术指标 每个门区最多可以设定30 个时间段&#xff0c;编号为00 至31&#xff0c;每个时间段包括开 始时间和结束时间。 &#xff08;系统出厂原始设置为24 小时通行&#xff0c;不予管制。如有特殊应 用&#xff0c;需进行时间管制时&#xff0c;此…