spring容器初始化时候的循环依赖问题

本文深入探讨Spring框架中循环依赖的解决机制,详细分析了通过三种缓存管理Bean对象的过程,揭示了Spring如何处理Bean之间的相互依赖问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们先看一个现象:

我在test方法中打印这两个对象A和B

ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("test.xml");
        A a=applicationContext.getBean("a", A.class);
        System.out.println(a.getB());
        B b=applicationContext.getBean("b",B.class);
        System.out.println(b.getA());

其中a和b这两个bean对象我在xml文件中进行了配置,并且这两个bena对象相互依赖:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="a" class="com.hosp.pojo.A" init-method="initMethod">
        <property name="b" ref="b" />
    </bean>
    <bean id="b" class="com.hosp.pojo.B" >
        <property name="a" ref="a"/>
    </bean>
</beans>

可以看到,很明显,A类中有一个属性指向了b的引用,同时B类中又有一个属性指向了a的引用。所以我们思考一个问题:

问题1:

spring容器在加载bena对象的时候,很明显这两个bean之间相互依赖了,如果这两个bean没有交给spring容器管理,我们自己去创建出这两个bean对象,我们自己怎么做到这种相互依赖的问题?

所以这要看我们是如何创建并管理benau对象的,并且如何做到依赖注入的。所以这里依赖注入的时候,绝对不能使用构造方法的方式,那样我们会不停的创建出一堆而且一摸一样的对象,我们只能用set属性的方式!我们需要一个容器去装载创建好的bean对象,去解决重复创建对象的问题,同时因为我们有了这个容器,我们只要创建出两个完整的A类和B类对象,a的属性b指向b,b的属性a指向a,就可以了。所以,spring容器中是如何管理创建出来的bean?如何识别出bean对象的所处的状态?如何进行依赖注入?接下来从这三个目标着手来研究源码。

1.spring容器中是如何管理创建出来的bean?

要知道如何管理,先要知道怎么创建出来的,所以这里不得不说初始化容器不得不看的一个函数refresh(),在这个方法里有一个finishBeanFactoryInitialization方法,在这一步

// Instantiate all remaining (non-lazy-init) singletons.
//实例化所有剩余的单例bean对象,所以这里也包括我们自定义的两个bean:a和b
finishBeanFactoryInitialization(beanFactory);

 进去之后还有一个方法:preInstantiateSingletons,初始化,再进,最终在DefaultListableBeanFactory的preInstantiateSingletons方法中,看到有这样一个方法:

public void preInstantiateSingletons() throws BeansException {
    //.....
    for (String beanName : this.beanDefinitionNames) {
        //...
            //初始化单例对象的时候,其实调用的是getBean方法,参数是beanname,也就是说传给他一个bean的名字,在这个方法内部,会创建出一个对象并添加到对象池中
            getBean(beanName);
        //...
    }
}

点进去,一路点getBean()->doGetBean(),

之后到了doGetBean()方法里,我们看到首先做的一部操作:检查缓存

Object sharedInstance = getSingleton(beanName);//检查单例缓存

 在getSingleton这个方法中,我们看到了BeanFactory中维护的三块缓存:

//后面会多次用到,getSingleton方法中,就是依次去这三块缓存中找有没有对应的bean
//而现在我们还没有对a、b这两个bean做任何的初始化,所以结果必然是null
singletonObjects、
earlySingletonObjects、
singletonFactories

 接下来源码非常多,我们不必在意,也没有必要全部每行代码都看懂,我们的目的是研究三块缓存怎么解决的循环依赖的问题,所以bean对象的创建、修改、以及三块缓存区内容的修改才是我们的关注点。。

后面有一大堆准备工作,无伤大雅,再后来有一个这样的方法:

sharedInstance = getSingleton(beanName, new ObjectFactory() {
					public Object getObject() throws BeansException {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					}
				});

 这里通过getSingleton这个方法去根据beanname创建出了一个bean对象并且赋值给了sharedInstance。(注意是一个匿名内部类,或者说是一种回调函数)关键是genSingleton这个方法可以理解为创建出了一个完整的bean对象。

的确我们在createBean函数中看到了,当中步骤有点多,省略没用的,

createBean->doCreateBean->createBeanInstance->instantiateBean->getInstantiationStrategy().instantiate->instantiateClass,最终通过反射创建了一个空对象,注意这个时候beanA的内容,完全是一个空对象,属性全都是默认值:

然后继续回到doCreateBean里面,现在创建了一个空对象之后,有一个判断:

earlySingletonExposure,当为true的时候,做了一件事:addSingletonFactory
if (earlySingletonExposure) {
   //....
   addSingletonFactory(beanName, new ObjectFactory() {
      public Object getObject() throws BeansException {
         return getEarlyBeanReference(beanName, mbd, bean);
      }
   });
}

在这里我们看到了之前用到的三级缓存,他把singletonFactory放到了第三季缓存里面,而这个singletonFactory是啥,又是一个回调函数,或者说一个匿名内部类的对象。同时registeredSingletons也把beanname加进去了。

protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

所以说为什么我们在getSingleton的方法上面看到了这样一段话:

Returns:
the registered singleton object

 在接下来,回到doCreateBean,看到这样一个方法:为a这个bean填充属性,而现在要填充的是b这个bean,所以这里做一个猜测:一定会再去创建b这个bean,也就是说,会重新执行到之前的getSingleton那个方法里面,接下来我们跟一遍看看是不是这样。。

populateBean(为beanA填充属性)->applyPropertyValues(为属性赋值)->resolveValueIfNecessary(解析value的值)

我们注意resolveValueIfNecessary这个方法,很有可能这里就要去创建beanB了,接着走,

resolveValueIfNecessary->resolveReference(解析引用)

这时看到了熟悉的方法:

//从beanfactory中获取bean对象,换句话说,这里终于又要创建beanB了,从而也就证实了之前的猜测

Object bean = this.beanFactory.getBean(refName);

所以到了这一步,我们又回到了之前的beanA创建的时候走的流程:

getSingleton判断三级缓存中有没有beanB->getSingleton->beanFactory匿名内部类的回调函数createBean->doCreateBean->createBeanInstance->instantiateBean(无参构造)

这里创建出了一个空的beanB,然后和beanA一样,把一个匿名内部类放到了singletonFactories缓存里面。

后面又是b的populateBean。。。

b的populateBean(给beanB填充属性)->applyPropertyValues(为beanB的属性赋值)->valueResolver.resolveValueIfNecessary(解析beanB的属性a的值)->resolveReference-> this.beanFactory.getBean(refName)(从容器中获取beanA)

注意,此时容器中已经有了beanA了,在三级缓存里面,所以调用singletonFactory.getObject方法可以获取到beanA。

现在获取到了beanA是为了继续给beanB的属性a填充值的,最终

//反射给beanB的a属性赋值了,到这一步,beanB的属性赋值工作已经完成了,也就是说这个时候beanB已经是一个完整的bean了
writeMethod.invoke(this.object, value);

 这个时候三级缓存中的内容是:singletonFactory有一个beanB,earlySingletonObjects有一个beanA

现在beanB已经是一个完整的bean了,所以下一步addSingleton把beanB放到singletonObjects也就是一级缓存里面。

现在一级缓存里面有一个beanB,而beanA还在二级缓存里面。

接下来,就是给beanA填充属性了,和beanB一样,最终beanA从二级缓存被移到了一级缓存了。结束。。。

 

5.如果采用不同的方式配置aop,aop的执行顺序会不会不一样?答:会

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @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);
		}
		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.
		boolean 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 " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_34116044

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值