Spring 循环依赖及三级缓存
Spring在启动过程中,使用到了三个map,称为三级缓存。Spring启动过程大致如下:1.加载配置文件2.解析配置文件转化beanDefination,获取到bean的所有属性、依赖及初始化用到的各类处理器等3.创建beanFactory并初始化所有单例bean4.注册所有的单例bean并返回可用的容器,一般为扩展的applicationContext一级缓存在第三步中,所有单例的bean初始
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,如果存在aop代理,则依赖的应该是代理对象,而不是原始的bean。而暴露原始bean是在单例bean初始化的第2步,填充属性第3步,生成代理对象第4步,这就矛盾了,A依赖到B并去解决B依赖时,要去初始化B,然后B又回来依赖A,而此时A还没有执行代理的过程,所以,需要在填充属性前就生成A的代理并暴露出去,第2步时机就刚刚好。
三级缓存的bean工厂getObject方式,实际执行的是getEarlyBeanReference,如果对象需要被代理(存在beanPostProcessors -> SmartInstantiationAwareBeanPostProcessor),则提前生成代理对象。
二级缓存
三级缓存已经解决所有问题了,二级缓存用来做什么呢?为什么三级缓存不直接叫做二级缓存?这个应该是在缓存使用时决定的:
三级缓存中提到出现循环依赖才去解决,也就是说出现循环依赖时,才会执行工厂的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();
...
}
更多推荐
所有评论(0)