Spring循環(huán)引用失敗問題源碼解析
前言:
之前我們有分析過Spring是怎么解決循環(huán)引用的問題,主要思路就是三級緩存;
Spring在加載beanA的時候會先調(diào)用默認的空構(gòu)造函數(shù)(在沒有指定構(gòu)造函數(shù)實例化的前提下)得到一個空的實例引用對象,這個時候沒有設(shè)置任何值,但是Spring會用緩存把它給提前暴露出來,讓其他依賴beanA的bean可以持有它提前暴露的引用;
比如 a 依賴b ,b依賴a,并且他們都是通過默認方法實例化,那么簡單流程是這樣的:
- ioc實例化a,a提前暴露自己的,然后填充屬性值,在填充屬性值的時候發(fā)現(xiàn)有個對象b,這個時候去容器里面取到b的引用,發(fā)現(xiàn)b還沒有被創(chuàng)建,那么就走實例化b的流程;
- 實例化b;流程跟a一樣;但是不同的是b填充屬性的時候,發(fā)現(xiàn)有引用a的實例,這個時候a已經(jīng)提前暴露了自己了,所以b可以直接在容器里面拿到a的引用;那么b就實例化并且也初始化完成了;
- 拿到b了之后,a就可以持有b的引用 ,整個流程就走完了;
具體詳細一點可以看這篇文章Spring-bean的循環(huán)依賴以及解決方式
Spring不能解決“A的構(gòu)造方法中依賴了B的實例對象,同時B依賴了A的實例對象”這類問題
這篇文章我想從源碼的角度來分析一下整個流程;
并且分析一下Spring為什么不能解決“A的構(gòu)造方法中依賴了B的實例對象,同時B依賴了A的實例對象”這類問題
例子
首先創(chuàng)建兩個bean類; CirculationA 有個屬性circulationB,并且有個構(gòu)造函數(shù)給circulationB賦值;
public class CirculationA { private CirculationB circulationB; public CirculationA(CirculationB circulationB) { this.circulationB = circulationB; } }
CirculationB 有個屬性circulationA,然后set方法
public class CirculationB { private CirculationA circulationA; public CirculationA getCirculationA() { return circulationA; } public void setCirculationA(CirculationA circulationA) { this.circulationA = circulationA; } }
SpringContextConfig.xml circulationa 用給定的構(gòu)造函數(shù)實例化;
circulationb 就用默認的實例化方法(默認的空構(gòu)造函數(shù))
<bean id="circulationa" class="src.bean.CirculationA"> <constructor-arg name="circulationB" ref="circulationb"/> </bean> <bean id="circulationb" class="src.bean.CirculationB" > <property name="circulationA" ref="circulationa"/> </bean>
好,例子準完畢,上面的例子是 circulationa的構(gòu)造函數(shù)里面有circulationb;
然后circulationb屬性里面有circulationa;
啟動容器
結(jié)果如下:
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationa' while setting bean property 'circulationA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationa' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationb' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationa' while setting bean property 'circulationA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
Disconnected from the target VM, address: '127.0.0.1:64128', transport: 'socket'
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at StartIOCUseDefaultListAbleBeanFactory.main(StartIOCUseDefaultListAbleBeanFactory.java:30)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'circulationb' defined in class path resource [config.xml]: Cannot resolve reference to bean 'circulationa' while setting bean property 'circulationA'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1531)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1276)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 17 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 27 more
報錯了,Spring它解決不了這種情況 Ok,源碼走起來: 為了節(jié)省篇幅我只貼重要代碼 第一步
加載circulationa AbstractBeanFactory
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { @Override public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } } }); } } public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { //在創(chuàng)建之前把beanName加入到正在創(chuàng)建中的屬性中singletonsCurrentlyInCreation; //但是這個是一個set,如果之前已經(jīng)加進去了,再進去就拋異常BeanCurrentlyInCreationException //Requested bean is currently in creation: Is there an unresolvable circular reference?")提示可能存在循環(huán)引用 beforeSingletonCreation(beanName); } protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } @Override protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { Object beanInstance = doCreateBean(beanName, mbdToUse, args); } protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { instanceWrapper = createBeanInstance(beanName, mbd, args); //....... // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } } protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); //因為circulationa是有構(gòu)造函數(shù)的,所以使用autowireConstructor if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } } //最終執(zhí)行 public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd, Constructor<?>[] chosenCtors, final Object[] explicitArgs) { //解析構(gòu)造函數(shù)參數(shù)值 minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); //...... //選擇對應(yīng)的策略來實例化對象;這里是生成正在的實例了。 //但是在這之前,構(gòu)造參數(shù)要拿到 beanInstance = this.beanFactory.getInstantiationStrategy().instantiate( mbd, beanName, this.beanFactory, constructorToUse, argsToUse); } }
省略....
最終調(diào)用BeanDefinitionValueResolver
/** * Resolve a reference to another bean in the factory. */ private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { String refName = ref.getBeanName(); refName = String.valueOf(doEvaluate(refName)); if (ref.isToParent()) { if (this.beanFactory.getParentBeanFactory() == null) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Can't resolve reference to bean '" + refName + "' in parent factory: no parent factory available"); } //!!!這里,要先去查找refName的實例 return this.beanFactory.getParentBeanFactory().getBean(refName); } else { Object bean = this.beanFactory.getBean(refName); this.beanFactory.registerDependentBean(refName, this.beanName); return bean; } } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex); } }
跟著上面的順序我們整理一下;
- 啟動容器,加載circulationa,因為是構(gòu)造函數(shù)生成,所以要先解析構(gòu)造函數(shù)的屬性,這時候發(fā)現(xiàn)有引用circulationb,那么通過getBean(circulationb)先拿到circulationb的實例;
- 如果拿到了,則生成circulationa的實例對象返回;但是這個時候代碼執(zhí)行circulationb的加載過程了;
circulationb加載分析
然后我們分析一下circulationb加載 circulationb跟circulationa差不多 加載circulationb,把它加入到正在創(chuàng)建的屬性中
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
然后用默認的方式創(chuàng)建實例; circulationa 是rautowireConstructor(beanName, mbd, ctors, args)創(chuàng)建的;這個方法需要先拿到構(gòu)造函數(shù)的值;所以執(zhí)行了調(diào)用getBean(circulationb)
circulationa是調(diào)用了instantiateBean;這個方法不需要提前知道屬性;它用默認的構(gòu)造函數(shù)生成實例;這時候的實例是沒有設(shè)置任何屬性的;
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); }
不過生成了實例之后,在doCreateBean方法中有一個populateBean;這個方法就是專門填充屬性值的,因為circulationb有circulationa的屬性; 所以會去容器里面取circulationa的引用;
但是circulationa這個時候還沒有成功創(chuàng)建實例?。灰驗樗€一直在等circulationb創(chuàng)建成功之后返回給它引用呢,返回了circulationa才能創(chuàng)建實例??;
這個時候circulationb沒有拿到circulationa,那么又會去調(diào)用getBean(circulationa); 大家想一想如果這樣下去就沒完沒了了啊; 所以Spring就拋出異常了 那么在哪里拋出異常呢? 在第二次調(diào)用getBean(circulationa)的時候會走到下面
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
因為circulationa之前加進來過一次啊,而且沒有創(chuàng)建成功是不會刪除的??;
現(xiàn)在又add一次,因為this.singletonsCurrentlyInCreation是一個set;
已經(jīng)存在的再次add會返回false;那么這段代碼就會拋出異常了;
Error creating bean with name 'circulationa': Requested bean is currently in creation: Is there an unresolvable circular reference?
情況就是這樣,只要是用構(gòu)造函數(shù)創(chuàng)建一個實例,并且構(gòu)造函數(shù)里包含的值存在循環(huán)引用,那么spring就會拋出異常;
所以如果有循環(huán)引用的情況請避免使用構(gòu)造函數(shù)的方式
以上就是Spring循環(huán)引用失敗問題源碼解析的詳細內(nèi)容,更多關(guān)于Spring循環(huán)引用失敗的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于bufferedreader的read()與readline()讀取出錯原因及解決
這篇文章主要介紹了bufferedreader的read()與readline()讀取出錯原因及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12Java之通過OutputStream寫入文件與文件復制問題
這篇文章主要介紹了Java之通過OutputStream寫入文件與文件復制問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04springboot中關(guān)于classpath:路徑使用及說明
這篇文章主要介紹了springboot中關(guān)于classpath:路徑使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09