SpringFramework源码分析(6):循环依赖的实现

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;
	}

SpringFramework系列目录

SpringFramework系列目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值