1 循环依赖的例子
在下面的代码中,类X中有一个属性Y,而类Y中也有一个属性X,X和Y构成了循环依赖的关系。
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class X implements InitializingBean {
@Autowired
private Y y;
@PostConstruct
public void initMethod() {
System.out.println("initMethod of X");
}
@Autowired
ApplicationContext applicationContext;
public X() {
System.out.println("Constructor of X");
}
public void xx() {
System.out.println("xxxxx");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet of X");
}
}
package com.woods.cyclic_dep;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class Y {
@Autowired
private X x;
@Autowired
private ApplicationContext applicationContext;
public Y() {
System.out.println("Constructor of Y");
}
public void yy() {
System.out.println("yyyy");
}
}
package com.woods.cyclic_dep;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.woods.cyclic_dep")
public class App {
}
package com.woods.cyclic_dep;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(App.class);
}
}
如果是我们自己写代码,我们可以通过new完对象再set的方式完成循环依赖,如下所示。
X x = new X();
Y y = new Y();
x.setY(y);
y.setX(x);
在循环依赖的示例代码中,X和Y对应的Bean都由SpringIoC容器管理,那Spring是如何处理这种循环依赖的呢?
2 Spring处理循环依赖的源码分析
2.1 背景知识
-
Spring默认按照BeanName的字典序进行Bean的实例化,在上面的例子中,会先实例化X,再示例化Y。
-
AutowiredAnnotationBeanPostProcessor用来处理@Autowired、@Value和JSR330的@Inject注解。
-
创建单实例Bean时,会先调用getBean方法,该方法尝试获取Bean,如果获取不到再进行创建。
-
DefaultListableBeanFactory中维护着几个重要的集合,这几个集合与循环依赖的处理有关。分别是:
- Set<String> singletonsCurrentlyInCreation,保存正在创建的Bean的BeanName。
- Map<String, Object> singletonObjects,key是BeanName,value是Bean实例。一级缓存,保存Spring容器中的单例。
- Map<String, ObjectFactory<?>> singletonFactories,key是BeanName,value是ObjectFactory(用例生成实例)。二级缓存。
- Map<String, Object> earlySingletonObjects,key是BeanName,value是半成品的Bean实例,即未完成初始化流程的Bean实例。三级缓存。
-
在DefaultSingletonBeanRegistry中有两个getSingleton方法,一个为getSingleton(String beanName),另一个为getSingleton(String beanName, ObjectFactory<?> singletonFactory)。在doGetBean方法中,先调用前一个getSingleton方法从Spring的缓存中取,如果取不到再执行第二个getSingleton方法进行Bean的创建。
-
getSingleton(String beanName, ObjectFactory<?> singletonFactory)。
该方法主要有三步。其中beforeSingletonCreation会把当前正在创建的BeanName加入到singletonsCurrentlyInCreation中,表示当前的Bean正在创建。创建完成后,会调用afterSingletonCreation方法,将BeanName从singletonsCurrentlyInCreation移除。- beforeSingletonCreation(beanName);
- singletonObject = singletonFactory.getObject();
- afterSingletonCreation(beanName);
-
2.2 整体流程
Spring处理循环依赖的整体流程如下所示。下面的图展示了Spring是如何创建单实例Bean以及如何解决循环依赖的。
我们在初始化单实例Bean中讲过,在AbstractApplicationContext的finishBeanFactoryInitialization中完成了单实例Bean的创建。对于上面的例子,Spring会先创建Bean X,在Bean X实例化过程中,到populateBean这一步时,会进行属性填充,进行Bean Y的创建。属性填充这一步是通过AutowiredAnnotationBeanPostProcessor完成的,AutowiredAnnotationBeanPostProcessor最终会调用到BeanFactory.getBean(y)进行BeanY的创建。
-
1 Bean x的初始化直到populateBean的调用栈如下,可以看到从finishBeanFactoryInitialization方法通过getBean(x)、doGetBean方法,调用到了getSingleton方法,在
一直调用到了populateBean方法,populateBean方法中完成了属性y的注入。
-
2 Bean x注入y的调用栈如下。可以看到这里主要是通过AutowiredAnnotationBeanPostProcessor的postProcessProperties方法,进行处理,然后调用getBean(y)方法尝试获取或创建Bean。
-
3 调用getBean(y)时,Spring也会执行与创建x类似的步骤,直到调用populateBean方法,这里会把x注入到y中。此时,也是调用BeanFactory的getBean(x)方法,接着调用getSingleton(x)方法,该方法会返回一个半成品的x,这个x也就是上面正在创建的x。
-
4 BeanY完成属性填充后,执行生命周期回调,最终完成了Bean实例的创建,加入到单例池中。
-
5 BeanY完成创建后,BeanX也就完成了populateBean方法。接着执行生命周期回调方法,也完成了Bean实例的创建,被加入到单例池中。
2.3 getSingleon(String beanName)方法详解
我们上面说过,在DefaultSingletonBeanRegistry中,有两个getSingleton方法。其中getSingleon(String beanName)方法是Spring在BeanFactory.getBean()的时候首先调用的方法,也就是从缓存中取。
这里的缓存分为三层。
- 一级缓存是singletonObjects,保存已经创建好的Bean实例。
- 二级缓存是singletonObjects,保存ObjectFactory,ObjectFactory是能够生成bean实例的工厂,当从一级缓存拿不到时,会从二级缓存通过工厂新建一个Bean,放到三级缓存中。
- 三级缓存是earlySingletonObjects,存放的是二级缓存工厂生成的对象,这个兑现是办成品的Bean实例,也就是未完成初始化流程的Bean实例
getSingleton(beanName)方法的总体流程是先从一级缓存取,如果没取到,并且beanName正在被创建,那么从三级缓存中取。如果还没取到,则通过二级缓存创建一个对象放到三级缓存中并返回。
getSingleton(beanName)的源码如下。
/**
* 先从一级缓存拿(singletonObjects),拿不到则从三级缓存拿(earlySingletonObjects),
* 拿不到则从二级缓存拿(singletonFactories)
*
* 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) {
// 一级缓存,从单例池中直接拿,如果能拿到,说明单例Bean已经创建好并放入单例池中了
Object singletonObject = this.singletonObjects.get(beanName);
// 如果这个时候是x注入y,创建y,然后y注入x,通过getSingleton获取x的时候,x不在singletonObjects中,但是x和y都在singletonsCurrentlyInCreation中
// 所以下面的if是true
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从三级缓存中拿,如果是x依赖y,y依赖x,x依赖z,z依赖x,那么z在获取x的时候,就可以直接从三级缓存中拿,而不需要通过二级缓存的工厂进行创建了
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 从二级缓存中拿一个ObjectFactory,可以获取到,由于这里的beanName是x,通过ObjectFactory可以产生一个x的半成品(没有经过完整生命周期的Bean)
// 为什么不在二级缓存中直接存半成品的Bean而是存一个Factory?
// 二级缓存中存一个工厂更加灵活,可以通过InstantiationAwareBeanPostProcessor干预要返回的Bean
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 通过ObjectFactory#getObject可以产生一个x的半成品(没有经过完整生命周期的Bean)
singletonObject = singletonFactory.getObject();
// 放入三级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从二级缓存中移除beanName对应的singletonFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
那么问题来了,为什么Spring要搞三个缓存?搞懂了这个问题,也就知道了getSingleton(beanName)这个方法的设计思想和源码。
首先,一级缓存singletonObjects是必须存在的,Spring可以把已经创建好的单实例Bean放在这个缓存中,这样我们在调用beanFactory的getBean方法时,就可以直接获取到相应的bean实例了。
其次,二级缓存中保存的是ObjectFactory对象,这个工厂用来生成bean实例。这样做的好处是可以通过SmartInstantiationAwareBeanPostProcessor进行扩展,也就是可以通过实现SmartInstantiationAwareBeanPostProcessor接口,来干预exposedObject的创建。
最后,二级缓存中ObjectFactory创建的对象会被保存到三级缓存中,这样就不需要每次都通过ObjectFactory来新创建对象,从而达到复用的效果。例如,如果是x依赖y,y依赖x;x依赖z,z依赖x。那么z在获取x的时候,就可以直接从三级缓存中拿,而不需要通过二级缓存的工厂进行创建了。
综上所述,Spring通过三个缓存,既提供了扩展性,又达到了对象复用的能力。
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));
}
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}