Spring循环依赖及解决方式

1. 什么是循环依赖?

      循环依赖其实就是循环引用,也就是两个或者两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:

Spring循环依赖及解决方式

注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。

Spring中循环依赖场景有: 

(1)构造器的循环依赖 

(2)field属性的循环依赖

其中,构造器的循环依赖问题无法解决,只能拋出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。

2. Spring怎么单例循环依赖

    Spring的循环依赖的理论依据基于Java的引用传递,当获得对象的引用时,对象的属性是可以延后设置的。(但是构造器必须是在获取引用之前)

Spring的单例对象的初始化主要分为三步:

Spring循环依赖及解决方式

(1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

(2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

(3)initializeBean:调用spring xml中的init 方法。

   从上面单例bean的初始化可以知道:循环依赖主要发生在第一、二步,也就是构造器循环依赖和field循环依赖。那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存

    这里会涉及到在spring内部所使用的两个内部属性,singletonFactories和earlySingletonObjects,这两个属性在类DefaultSingletonBeanRegistry中被定义,定义如下:

Spring循环依赖及解决方式

     singletonFactories : 单例对象工厂的cache 

     earlySingletonObjects :提前暴光的单例对象的Cache, 用于存储在创建Bean早期对创建的原始bean的一个引用,注意这里是原始bean,即使用工厂方法或构造方法创建出来的对象

     singletonObjects:单例对象的cache

     那么再来看下这两个对象如何进行协作:

方法1:

Spring循环依赖及解决方式

    对象信息以ObjectFactory的形式被放入singletonFactories中,这时earlySingletonObjects中肯定没有此对象(因为remove)。

方法2:

Spring循环依赖及解决方式

    在一定条件下(allowEarlyReference为true)的条件下,对象从singleFactories中的ObjectFactory中被取出来,同时remove掉,被放入earlySingletonObjects中。这时,earlySingletonObjects就持有对象信息了;当然,如果allowEarlyReference为false的情况下,且earlySingletonObjects本身就没有持有对象的情况下,肯定不会将对象从objectFactory中取出来的。这个很重要,因为后面将根据此信息进行循环引用处理

方法3:

Spring循环依赖及解决方式

     对象被加入到singletonObjects中,同时singletonFactories和earlySingletonObjects中都remove掉持有的对象(不管持有与否),这就表示在之前的处理中,这只相当于一个临时容器,处理完毕之后都会remove掉

     那么,我们来看这3个方法是不是按照先后顺序被调用的呢。代码顺序如下所示:

类AbstracBeanFactory获取bean调用点M1:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
	/* 
	 * 其他逻辑
	 */

	if (mbd.isSingleton()) {
		sharedInstance = getSingleton(beanName, () -> {
			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;
			}
		});
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}

	/* 
	 * 其他逻辑
	 */

}

进入getSingleton方法, 调用点M-2

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	/*
	* 其他逻辑,主要是保证单例的唯一性
	*/
	try {
		singletonObject = singletonFactory.getObject();
		newSingleton = true;
	}
	catch (IllegalStateException ex) {
		// do something
	}
	catch (BeanCreationException ex) {
		// do something
	}
	finally {
		// do something
	}
	if (newSingleton) {
		addSingleton(beanName, singletonObject);
	}
}

查看singletonFactory.getObject(),即createBean(beanName, mbd, args),最终转向doCreateBean方法,调用点:M-3

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
	// .....
	final Object bean = instanceWrapper.getWrappedInstance();

	// .....
	if (earlySingletonExposure) {
		if (logger.isDebugEnabled()) {
			logger.debug("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// populateBean(beanName, mbd, instanceWrapper); 
	// initializeBean(beanName, exposedObject, mbd);
}