首先,循环依赖是啥,用白话说,最简单的案例就是A中有B,而B中又有A,这样一个例子并不能说明什么,但是Spring容器创建Bean是有顺序的。
假设有A、B两类
public class A {
B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
<bean id = "a" class="test.v1.vo.A" scope="singleton">
<property name="b" ref="b"></property>
</bean>
<bean id = "b" class="test.v1.vo.B" scope="singleton">
<property name="a" ref="a"></property>
</bean>
这个时候我们调用
A a = (A)bf.getBean("car");
我们来回顾一下Spring容器获取Bean过程,当然我们只讲重点。首先因为A是单例,而Spring中有三级缓存来存储单例Bean
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 HashMap<>(16);
但是我们假设的大前提是A是第一次创建,所以三级缓存中都不存在该单例,由于getBean顺序是首先从三级缓存中获取单例,再到从parentBeanFactory获取,如果仍然获取不到则这时候则开始创建Bean
doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 创建Bean.
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);
}
}
doCreateBean
public class BeanDefinitionTest {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
....
//初始化
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
....
// 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);
}
}
...
}
又到了创建Bean三步走,createBeanInstance、populateBean是创建Bean非常重要的两个步骤,createBeanInstance实质是调用A类的构造函数,Spring则规定只能处理Setter依赖,而构造器依赖则不行,原因很简单,解决循环依赖的思路就是不等bean创建完成就会创建将bean引用曝光,而获取bean引用必须完成构造器函数。所以我们需要关注Setter依赖,也就是属性注入这部分。
populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
...
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
//根据属性name装配
autowireByName(beanName, mbd, bw, newPvs);
}
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
//根据属性type装配
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
...
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
autowireByName
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
//getBean
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (logger.isTraceEnabled()) {
logger.trace("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
这里我们为什么只需要看autowireByName这个函数呢,因为装配的时候ByName或者ByType最终还是会调用getBean方法,所以我们这里选取装配逻辑最简单的方法。autowireByName是根据属性的名称,也就是XML配置中<property>标签中的<name>的值,最终调用getBean(String name)来获取bean进行注入。那么根据XML的配置,A需要注入属性B,也就是调用getBean("b")。
与上文一样,B也是第一次创建,容器中缓存也不存在B,B也需要像A一样从头开始创建,当B需要注入属性时,根据XML文件的配置需要注入属性A,进入autowireByName方法然后调用getBean("a")。
然而,这次getBean("a")就跟上文a的过程就有所区别了,Spring解决循环依赖的原则就是不等bean创建完成就将bean的引用暴露(缓存)。
暴露时机(doCreateBean)
public class BeanDefinitionTest {
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
....
//实例化后
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));
}
//填充属性前
...
}
addSingletonFactory
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);
}
}
}
我们可以看到这个方法的参数是ObjectFactory,为什么它并不是一个Object呢?
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
ObjectFactory是一个接口,调用addSingletonFactory 时利用lambda表达式实现了getObject方法,但是事情并不是那么简单,实现getObject时使用了回调函数getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
这里回调函数利用后置处理器对bean进行改动,这里其实就是AOP切入的时机,当然这节内容跟AOP无关。但是,不管怎么说,Spring通过实现ObjectFactory的方法将bean存储到SingletonFactory(第三级缓存)。
既然缓存有a(我们需要注意 a暴露的时机是在实例化后注入属性前,所以a本身是不完整的),所以b注入属性时调用getBean("a")可以直接从缓存中取了
doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
...
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
...
return (T) bean;
}
getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//尝试从一级缓存中取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//尝试从二级缓存中取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//尝试从三级缓存中取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
当然很明显,a最终会从三级缓存中获取到,并且调用getObject方法,并且会进入二级缓存。这里有人会说,二级缓存的缓存作用是什么,有什么用?首先我们要明白,Spring团队是怎么定义二级缓存时
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
有人会说,“直接放入一级缓存不就得了”,或者换种方式说,“其实解决循环依赖二级缓存就够了”。可能我的水平不够,我现在也没发现二级缓存的作用在哪,但是二级缓存的存在应该跟它的定义有关(也就是存储着还处于创建状态的bean),可能解决循环依赖二级缓存足够了,但是我认为在学习他人框架源码的时候,我们应该尝试去理解别人的意境,只要不是错误,在这个角度出发,如果作者认为,我就是打算用二级缓存来存储这些未成型的bean,或许将bean的生命周期跟这些缓存挂钩,这本身不是一种错误,不要钻牛角尖。
既然getBean("a")能通过缓存返回,那么b的属性注入就能顺利完成,回顾整个过程,它本身就是递归,b的完成会返回到第一次创建a的过程,最终a也能完成属性注入。循环依赖也就解决,其实解决循环依赖的思路非常简单。