一、spring 概述
二、XML配置spring容器
三、Spring依赖注入类型
四、Spring依赖注入原理分析
五、Spring循环依赖解决方案
Spring循环依赖解决方案
Spring Ioc
中Bean
处于单例模式且setter
注入方法下可以解决循环依赖依赖。对于单例作用域来说,Spring
容器在整个生命周期内,有且只有一个Bean
对象,所以很容易想到这个对象应该存在于缓存中。Spring
为了解决单例Bean
的循环依赖问题,使用了三级缓存。
1.1 三级缓存结构
所谓的三级缓存,在Spring
中表现为三个Map
对象,如代码清单1-17
所示。这三个Map
对象定义在DefaultSingletonBeanRegistry
类中,该类是DefaultListableBeanFactory的父类。
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
代码清单1-16
中singletonObjects
变量是第一级缓存,用来持有完整的Bean
实例。而earlySingletonObjects
中存放的是那些提前暴露的对象,也就是已经创建但还没有完成属性注入的对象,属于第二级缓存。最后的singletonFactories
存放用来创建earlySingletonObjects
的工厂对象,属于第三级缓存。
三级缓存如何协作解决循环依赖问题的呢?首先我们看一下获取Bean
的代码流程。
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 首先从一级缓存 singletonObjects 中获取
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
// 如果获取不到,则从二级缓存中 earlySingletonObjects 中获取
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果还获取不到,就从三级缓存 singletonFactory 中获取
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 从三级缓存中获取成功,就把对象从第三级缓存移动到第二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
如代码清单1-17
所示,我们不难理解对三级缓存的依次访问过程,但也可能还不是很理解Spring
为什么要这样设计。事实上,解决循环依赖的关键还是要围绕Bean
的生命周期。在实例化Bean
的过程中,
org.springframework.beansfactory.support.AbstractAutowireCapableBeanFactory
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
......
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args); // 创建 Bean 实例
}
Object bean = instanceWrapper.getWrappedInstance();
......
// 针对循环依赖问题,暴露单例工厂类
// 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); // 初始化 Bean 实例,注入属性值
exposedObject = initializeBean(beanName, exposedObject, mbd); // 初始化 Bean 实例,执行工厂回调,比如 init 和 bean post processors
}
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);
}
}
......
return exposedObject;
}
Spring
解决循环依赖的诀窍就在于singletonFactories
这个第三级缓存,上述addSingletonFactory
方法用于初始化这个第三级缓存中的数据,如代码清单1-19
所示:
org.springframework.beansfactory.support.AbstractAutowireCapableBeanFactory
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
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);
}
}
}
这段代码的执行时机是在已经通过构造函数创建Bean
,但还没有完成对Bean
中完整属性的注入的时候。换句话说,Bean
已经可以被暴露出来进行识别了,但还不能正常使用。那为什么使用这种机制就可以解决循环依赖问题呢?
1.2 循环依赖解决方案
看下面使用setter
循环依赖注入代码:
public class ClassA {
private ClassB classB;
@Autowired
public void setClassB(ClassB classB) {
this.classB = classB;
}
}
public class ClassB {
private ClassA classA;
@Autowired
public void setClassA(ClassA classA) {
this.classA = classA;
}
}
现在假设我们先初始化ClassA
,步骤如下,详细步骤如下图所示。
ClassA
首先通过createBeanInstance
方法创建了实例,并且将这个实例暴露到第三级缓存(singletonFactories
)中,对应图中步骤1-7
。- 然后,
ClassA
尝试通过populateBean
方法注入属性,发现自己依赖ClassB
这个属性,就会尝试去获取ClassB
的实例,对应图中步骤8-15
。 - 显然,这时候
ClassB
还没有被创建,所以要走创建流程,对应图中步骤16-28
。 ClassB
在初始化的时候发现自己依赖了ClassA
,就会尝试从第一级缓存(singletonObjects
)去获取ClassA
的实例。因为ClassA
这时候还没有被创建完毕,所以它在第一级缓存和第二级缓存中都不存在。当尝试访问第三级缓存时,因为ClassA
已经提前暴露了,所以ClassB
能够通过singletonFactories
拿到ClassA
对象并顺利完成所有初始化流程,对应图中步骤29-40
。ClassB
对象创建完成之后会被放到第一级缓存中,对应步骤41-42
。- 随后返回创建的
ClassB
,回到ClassA populate
流程,随后完成ClassA
的创建流程。