Spring 循环依赖及三级缓存

Spring在启动过程中,使用到了三个map,称为三级缓存。


Spring启动过程大致如下:
1.创建beanFactory,加载配置文件
2.解析配置文件转化beanDefination,获取到bean的所有属性、依赖及初始化用到的各类处理器等
3.刷新beanFactory容器,初始化所有单例bean
4.注册所有的单例bean并返回可用的容器,一般为扩展的applicationContext

一级缓存

在第三步中,所有单例的bean初始化完成后会存放在一个Map(singletonObjects)中,beanName为key,单例bean为value。

第三步单例bean的初始化过程大致如下:
0.标记bean为创建中
1.new出bean对象
2.如果支持循环依赖则生成三级缓存,可以提前暴露bean
3.填充bean属性,解决属性依赖
4.初始化bean,处理Aware接口并执行各类bean后处理器,执行初始化方法,如果需要生成aop代理对象
5.如果存在循环依赖,解决之 – 这里有点问题,这一步是如果之前解决了aop循环依赖,则缓存中放置了提前生成的代理对象,然后使用原始bean继续执行初始化,所以需要再返回最终bean前,把原始bean置换为代理对象返回
6.此时bean已经可以被使用,进行bean注册(标记)并注册销毁方法。
7.将bean放入容器中(一级缓存),移除创建中标记及二三级缓存(后面再具体分析)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

循环依赖及三级缓存

根据以上步骤可以看出bean初始化是一个相当复杂的过程,假如初始化A bean时,发现A bean依赖B bean,即A初始化执行到了第3步填充属性,需要注入B bean,此时B还没有初始化,则需要暂停A,先去初始化B,那么此时new出来的A对象放哪里,直接放在容器Map里显然不合适,半残品怎么能用,所以需要提供一个可以标记创建中bean(A)的Map,可以提前暴露正在创建的bean供其他bean依赖,而如果初始化A所依赖的bean B时,发现B也需要注入一个A的依赖(即发生循环依赖),则B可以从创建中的beanMap中直接获取A对象(创建中)注入A,然后完成B的初始化,返回给正在注入属性的A,最终A也完成初始化,皆大欢喜。

如果配置不允许循环依赖,则上述缓存就用不到了,A 依赖B,就是创建B,B依赖C就去创建C,创建完了逐级返回就行,所以,一级缓存之后的其他缓存(二三级缓存)就是为了解决循环依赖!而配置支持循环依赖后,就一定要解决循环依赖吗?肯定不是!循环依赖在实际应用中也有,但不会太多,简单的应用场景是: controller注入service,service注入mapper,只有复杂的业务,可能service互相引用,有可能出现循环依赖,所以为了出现循环依赖才去解决,不出现就不解决,虽然支持循环依赖,但是只有在出现循环依赖时才真正暴露早期对象,否则只暴露个获取bean的方法,并没有真正暴露bean,因为这个方法不会被执行到,这块的实现就是三级缓存(singletonFactories),只缓存了一个单例bean工厂

配置了循环依赖才会启用缓存,此处只暴露了bean工厂,没有真正暴露bean

这个bean工厂不仅可以暴露早期bean还可以暴露代理bean,如果存在aop代理,则依赖的应该是代理对象,而不是原始的bean。而暴露原始bean是在单例bean初始化的第2步,填充属性第3步,生成代理对象第4步,这就矛盾了,A依赖到B并去解决B依赖时,要去初始化B,然后B又回来依赖A,而此时A还没有执行代理的过程,所以,需要在填充属性前就生成A的代理并暴露出去,第2步时机就刚刚好。

三级缓存的bean工厂getObject方式,实际执行的是getEarlyBeanReference,如果对象需要被代理(存在beanPostProcessors -> SmartInstantiationAwareBeanPostProcessor),则提前生成代理对象。
使用缓存解决循环依赖时,检查是否生成aop代理

二级缓存

三级缓存已经解决所有问题了,二级缓存用来做什么呢?为什么三级缓存不直接叫做二级缓存?这个应该是在缓存使用时决定的:
二三级缓存由来?
三级缓存中提到出现循环依赖才去解决,也就是说出现循环依赖时,才会执行工厂的getObject生成(获取)早期依赖,这个时候就需要给它挪个窝了,因为真正暴露的不是工厂,而是对象,所以需要使用一个新的缓存保存暴露的早期对象(earlySingletonObjects),同时移除提前暴露的工厂,也不需要在多重循环依赖时每次去执行getObject(虽然个人觉得不会出问题,因为代理对象不会重复生成,详细可以了解下代理里面的逻辑,如wrapIfNecessary)。

总结

经过分析可以看到:
1.不支持循环依赖情况下,只有一级缓存生效,二三级缓存用不到
2.二三级缓存就是为了解决循环依赖,且之所以是二三级缓存而不是二级缓存,主要是可以解决循环依赖对象需要提前被aop代理,以及如果没有循环依赖,早期的bean也不会真正暴露,不用提前执行代理过程,也不用重复执行代理过程。

此篇修改多次,每次有惊喜!循环依赖这块的理解希望不会再有理解错误或者不到位的地方,如仍有错误,欢迎指出,欢迎探讨!


补充

有人觉得三级缓存没必要,存在aop代理时,直接生成代理对象并暴露出去,生成二级缓存就够了。

这个结论没问题!代码改一改完全可以满足需求

为什么Spring不这么做呢?我认为这是Spring发展过程中产生的历史问题,早期的版本应该是不支持循环依赖的!后来遇到了循环依赖的问题,Spring为了尽可能小的影响原来的核心代码,就对当时AOP代理过程做了扩展,而不是推翻重写。

Spring正常的代理应该是发生在bean初始化后,由AbstractAutoProxyCreator.postProcessAfterInitialization处理。而循环依赖要求bean在填充属性前就提前生成代理,所以Spring在代码中开了个口子,循环依赖发生时,提前代理,没有循环依赖,代理方式不变,依然是初始化以后代理,所以不是不能直接提前生成代理,而是所有bean都提前生成代理,那AbstractAutoProxyCreator.postProcessAfterInitialization直接废了,相当于把原本的逻辑推翻重写了,这么做只是为了解决循环依赖得不尝试,没有完全必要的情况下对核心代码大改甚至推翻重写是一种大忌。

三级缓存的实现提供了提前生成代理的口子,而不是直接生成代理,只有发生循环依赖执行getObject才会执行代理,达到上述循环依赖发生时,提前代理,没有循环依赖,代理方式不变,依然是初始化以后代理的目的。

当前以上都只是猜测,源码中没有找到说明,百度也暂时没找到答案,只能翻一翻历史版本看看能不能找到点蛛丝马迹。
就从三级缓存处理入手。SmartInstantiationAwareBeanPostProcessor,这个接口提供了获取早期以来,版本 是2.0.3 : @since 2.0.3。在spring-beans jar包中。

/**
 * Extension of the {@link InstantiationAwareBeanPostProcessor} interface,
 * adding a callback for predicting the eventual type of a processed bean.
 *
 * <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
 * internal use within the framework. In general, application-provided
 * post-processors should simply implement the plain {@link BeanPostProcessor}
 * interface or derive from the {@link InstantiationAwareBeanPostProcessorAdapter}
 * class. New methods might be added to this interface even in point releases.
 *
 * @author Juergen Hoeller
 * @since 2.0.3
 * @see InstantiationAwareBeanPostProcessorAdapter
 */
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
	...
}

那就查看下spring-beans 2.0.2的jar包。发现没有找到SmartInstantiationAwareBeanPostProcessor这个类!
那这个版本中怎么代理的呢,继续查看AbstractAutoProxyCreator,这个类在spring-aop jar包中,查看对应的2.0.2jar包,已经下载不到源码了…那就直接看class吧。

public abstract class AbstractAutoProxyCreator extends ProxyConfig implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Ordered {
    ...

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (!this.targetSourcedBeanNames.contains(beanName) && !this.isInfrastructureClass(bean.getClass(), beanName) && !this.shouldSkip(bean.getClass(), beanName)) {
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            return specificInterceptors != DO_NOT_PROXY ? this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)) : bean;
        } else {
            return bean;
        }
    }

    ...
}

精简版源码!直接生成代理和最新版代码对比下

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}

2.0.2版本的Spring代理不仅没有考虑早期依赖,也没有getEarlyBeanReference方法,是不支持循环依赖的!!!

所以,2.0.3为了支持循环依赖,增加了一堆操作,产生了三级缓存,如果只用二级缓存,就得对原有代码大动刀,不符合Spring风格和原则,应该是这样的吧!

最后再来赏析2.0.2 Spring 创建bean代码以及单例bean工厂定义

protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) throws BeanCreationException {
	if (mergedBeanDefinition.getDependsOn() != null) {
		for(int i = 0; i < mergedBeanDefinition.getDependsOn().length; ++i) {
			this.getBean(mergedBeanDefinition.getDependsOn()[i]);
		}
	}

	if (this.logger.isDebugEnabled()) {
		this.logger.debug("Creating instance of bean '" + beanName + "' with merged definition [" + mergedBeanDefinition + "]");
	}

	Class beanClass = this.resolveBeanClass(mergedBeanDefinition, beanName);

	try {
		mergedBeanDefinition.prepareMethodOverrides();
	} catch (BeanDefinitionValidationException var13) {
		throw new BeanDefinitionStoreException(mergedBeanDefinition.getResourceDescription(), beanName, "Validation of method overrides failed", var13);
	}

	String errorMessage = null;

	try {
		errorMessage = "BeanPostProcessor before instantiation of bean failed";
		if (beanClass != null && !mergedBeanDefinition.isSynthetic()) {
			Object bean = this.applyBeanPostProcessorsBeforeInstantiation(beanClass, beanName);
			if (bean != null) {
				bean = this.applyBeanPostProcessorsAfterInitialization(bean, beanName);
				return bean;
			}
		}

		errorMessage = "Instantiation of bean failed";
		BeanWrapper instanceWrapper = null;
		if (mergedBeanDefinition.isSingleton()) {
			synchronized(this.getSingletonMutex()) {
				instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
			}
		}

		if (instanceWrapper == null) {
			instanceWrapper = this.createBeanInstance(beanName, mergedBeanDefinition, args);
		}

		Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null;
		if (mergedBeanDefinition.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName)) {
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
			}

			this.addSingleton(beanName, bean);
		}

		errorMessage = "Initialization of bean failed";
		boolean continueWithPropertyPopulation = true;
		if (!mergedBeanDefinition.isSynthetic()) {
			Iterator it = this.getBeanPostProcessors().iterator();

			while(it.hasNext()) {
				BeanPostProcessor beanProcessor = (BeanPostProcessor)it.next();
				if (beanProcessor instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)beanProcessor;
					if (!ibp.postProcessAfterInstantiation(bean, beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}

		if (continueWithPropertyPopulation) {
			this.populateBean(beanName, mergedBeanDefinition, instanceWrapper);
		}

		Object originalBean = bean;
		bean = this.initializeBean(beanName, bean, mergedBeanDefinition);
		if (!this.allowRawInjectionDespiteWrapping && originalBean != bean && mergedBeanDefinition.isSingleton() && this.hasDependentBean(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans " + this.getDependentBeans(beanName) + " in its raw version as part of a circular reference, " + "but has eventually been wrapped (for example as part of auto-proxy creation). " + "This means that said other beans do not use the final version of the bean. " + "This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
		} else {
			this.registerDisposableBeanIfNecessary(beanName, originalBean, mergedBeanDefinition);
			return bean;
		}
	} catch (BeanCreationException var14) {
		throw var14;
	} catch (Throwable var15) {
		throw new BeanCreationException(mergedBeanDefinition.getResourceDescription(), beanName, errorMessage, var15);
	}
}
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private final Map singletonCache = CollectionFactory.createLinkedMapIfPossible(16);
    private final Set singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet());
    private boolean singletonsCurrentlyInDestruction = false;
    private final Map disposableBeans = CollectionFactory.createLinkedMapIfPossible(16);
    private final Map dependentBeanMap = new HashMap();
    ...
}
  • 197
    点赞
  • 888
    收藏
    觉得还不错? 一键收藏
  • 71
    评论
Spring循环依赖三级缓存是指在实例化Bean的过程中,Spring框架内部维护了三个缓存Map,分别是singletonObjects、earlySingletonObjects和singletonFactories。 1. singletonObjects缓存:存放已经完全初始化的Bean实例,包括在初始化过程中有循环依赖的Bean 2. earlySingletonObjects缓存:存放已经进行了实例化但是还未完成初始化的Bean实例,也包括在初始化过程中有循环依赖的Bean 3. singletonFactories缓存:存放Bean实例的Factory对象,用于解决循环依赖的问题,当需要解决循环依赖时,由Factory对象提供未完成的Bean实例。 通过这三个缓存,Spring框架可以在Bean实例化的过程中解决循环依赖的问题。当Bean A依赖Bean B,同时Bean B也依赖Bean A时,Spring框架会通过缓存机制来避免无限递归的循环依赖。具体流程如下: 1. 当需要实例化Bean A时,Spring框架会先从singletonObjects缓存中查找是否已经存在该Bean实例,如果存在,则直接返回该实例。 2. 如果singletonObjects缓存中不存在Bean A实例,则会从earlySingletonObjects缓存中查找是否存在该实例。如果存在,则将其提供给Factory对象创建Bean A实例并返回。 3. 如果earlySingletonObjects缓存中也不存在Bean A实例,则会从singletonFactories缓存中查找是否存在该实例的Factory对象,如果存在,则使用该Factory对象创建Bean A实例并返回。 4. 如果singletonFactories缓存中也不存在Bean A实例的Factory对象,则需要创建一个新的Factory对象,同时将其存放到singletonFactories缓存中。这个新的Factory对象会提供一个未进行初始化的Bean A实例,同时缓存到earlySingletonObjects中。 5. 当Bean A实例被完全初始化后,会将其从earlySingletonObjects缓存中移动到singletonObjects缓存中,以供其他Bean依赖使用。 通过三级缓存的机制,Spring框架可以解决循环依赖的问题,并在Bean实例化过程中避免无限递归的循环依赖

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 71
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值