spring 循环依赖深度解析

问题产生原因

循环依赖是什么

循环依赖是指 spring 中,两个类各自注入对方实例到自身字段的一种情况。由于双方互相注入,因此形成了循环依赖。比如 beanA 中注入了 B 实例,B 中注入了 A 实例。如下代码所示:

// A依赖了B
class BeanA{
    @Autowired
    public BeanB b;
} 

// B依赖了A
class BeanB{
	@Autowired
    public BeanA a;
}

为什么这样会产生问题?

正常的互相依赖是不会产生问题的,如下所示

	BeanA a = new BeanA();
	BeanB b = new BeanB();

	a.b = b;
	b.a = a;

而 spring 的创建流程可以简化为以下三步,实例化、填充Bean,初始化。

在填充 bean 时,会执行 Autowired 注入,如果在容器中没找到对应的对象,则会走创建流程,因此导致循环依赖问题,如下所示:

spring 的解决方案

三级缓存

在 Spring 中是通过三级缓存去处理的。三级缓存如下所示:
image.png
singletonObjects:一级缓存,存放成品的 bean。可以对外使用
earlySingletonObjects:二级缓存,在被提前引用后,存放被代理后的对象(如果有被代理的话),不能对外使用
singletonFactories:三级缓存,存放对象工厂,用于获取被提前引用的 bean,主要解决循环依赖问题,不能对外使用

在加入了缓存后,处理流程大致如下:

上图没有太明显的执行次序,这里简单补充一张创建过程,可以对照着看一下

由上图可以得知,在加入三级缓存后,创建 B 时,填充 bean 流程不会再次执行 spring 的实例化A流程,而是从缓存中获取对象工厂,由此获取代理对象并注入,解决了循环依赖的问题。

关键源码解析

  1. 创建 bean 流程
    1. 源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// 创建 bean 流程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

        // 实例化 bean
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		// 省略。。。

        // 放入三级缓存
		// 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");
			}
            // 将实例化后的 bean 包装成 ObjectFactory 对象放入三级缓存
            // getEarlyBeanReference 方法为:调用 bean 后处理器,提前获取代理对象
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
            // 填充 bean,调用 bean后处理器,执行 autowired 等注解的解析注入
			populateBean(beanName, mbd, instanceWrapper);
            // 初始化 bean
			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);
			}
		}
    
        if (earlySingletonExposure) {
    			Object earlySingletonReference = getSingleton(beanName, false);
                // 如果被提前引用,则获取被提前引用的对象(代理增强),然后将该对象返回
    			if (earlySingletonReference != null) {
    				if (exposedObject == bean) {
    					exposedObject = earlySingletonReference;
    				}
    				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    				// 省略。。。
    			
    		}
        // 省略。。。
		return exposedObject;
	}
  1. 依赖注入时,从缓存中获取 bean
    1. 源码位置:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object beanInstance;

        // 这里 getSingleton,从三级缓存中获取 beanA
    	// 然后调用三级缓存中的对象工厂,提前创建代理对象,并放入二级缓存中
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// 省略。。。
			try {
				// 省略。。。

				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
                            // 如果是单例 bean,缓存中没有该对象,则走创建流程
							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;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					
				// 省略。。。
			}
			// 省略。。。
		}

		return adaptBeanInstance(name, beanInstance, requiredType);
	}
  1. 从缓存中获取 bean
    1. 源码位置:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    // 逐级查找,先从一级缓存中查
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 再从二级缓存中查找
        singletonObject = this.earlySingletonObjects.get(beanName);
        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) {
                        	// 如果有的话,则从对象工厂中获取被提前引用的 bean
                            singletonObject = singletonFactory.getObject();
                            // 然后放入二级缓存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}
  1. 通过对象工厂获取被提前引用的 bean
    1. 源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
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;
}

b. 补充,主要使用该方法进行代理增强:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference

public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
        // 如果需要代理则进行代理增强
		return wrapIfNecessary(bean, beanName, cacheKey);
}

注意事项

  1. 如果没有循环依赖,则正常的代理增强不会通过对象工厂创建,而是通过 bean 后处理器 BeanPostProcessor.postProcessAfterInitialization 方法创建,具体实现参考源码:org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
  2. spring 通过三级缓存区分成型的 bean、已经被提前引用(代理)后的 bean、未成型且未被提前引用的 bean。因此如果已经被提前引用(代理增强)了,则不会被重复代理

为什么这么解决

** 以下内容为个人理解,仅供参考,如有错误之处,还望指出,互相交流 _ **

仅一级缓存的问题

个人认为,实际上只要有一级缓存也能解决循环依赖的问题。如下图所示:

将 bean 实例化好后,在填充前就放入到缓存中,这样就算需要依赖注入,也可以直接从缓存中获取,但这种需要一级缓存中允许存放未成品的 bean。
如果一级缓存允许存放未成品的 bean,那么一些懒加载的 bean,可能会在容器提供服务后被并发调用创建,在这种情况下,可能会将未成品的 bean 提供出去使用,由此会带来一些问题。如下图所示
只有一级缓存的情况:

仅二级缓存的解决方案

而二级缓存则是防止这种情况,将未成品的 bean 放入到二级缓存中,成品的 bean 放入到一级缓存,这样就不会产生这个问题,也能解决循环依赖。
有二级缓存的情况:

代理问题的解决

如果仅有二级缓存,那么出现循环依赖中需要被代理时,则需要解决两个问题

  1. 代理类何时创建?
  2. 放入二级缓存的是代理类还是原始类?

代理类的创建时机可以有两个:

  1. 实例化后,直接产生代理对象并放入二级缓存;

  2. 在被提前引用时,创建代理类。
    第二种方案在仅有二级缓存中行不通,因为如果是被提前引用时再创建,那么可能会有以下问题:

  3. 被提前引用后,如果将代理类放入二级缓存中做替换,那么其他类需要提前引用时,如何区分是否已经被代理,不需要再被代理?

  4. 如果将代理类放入一级缓存,则可能会出现前面说的容器初始化完成后的并发调用问题

  5. 如果是每次被提前引用时,都创建新的代理对象,则对象引用错误

方案一则没问题,后续流程可以正常执行,但是 spring 并没有采用这种方案,而是使用了三级缓存去解决

三级缓存的加入

在三级缓存的设计中,第三级缓存存放 ObjectFactory,二级缓存存放被提前创建的代理 bean,一级缓存存放成品 bean。

这个设计应该是 spring 为了不影响其他正常的 bean 的创建流程所做的特殊处理。所以在设计时,并没有采用二级缓存,提前创建代理类的方式,去打乱创建流程。而是采用三级缓存的方式,对这些特殊的 bean 做的特殊处理(如果是正常的创建流程,则会在填充和初始化 bean 后,通过 bean 后处理器去创建代理对象)。

当出现循环依赖时,则通过三级缓存中的 ObjectFactory 去获取被代理的 bean,提供依赖注入使用,然后将其存放到二级缓存中,表示该类已经执行过代理增强方法。如果和其他类还存在循环依赖,则直接引用二级缓存中已经被代理过的 bean 即可。

以上。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值