在上一篇中我们详细讨论了Spring中Bean的生命周期,对三级缓存和循环依赖的问题有过一些比较粗略的讲解。那么这篇结合自定义的代码和Spring的源码一起深入了解下Spring的三级缓存及循环依赖问题。
首先我们要知道什么是循环依赖问题?
此时图中是一个闭环。如果想解决这个闭环问题,那么必须要保证不会出现第二次创建A对象这个步骤,也就是说从容器中必须能够获取到A这个对象。在Spring中,对象的创建分为实例化和初始化,实例化完成初始化尚未开始的时候,已实例化的对象是可以直接给其它对象引用的。所以可以通过把已实例化未初始化的对象提前暴露出去,让其它对象可以引用,这样就解决了这个闭环的问题,而已实例化未初始化的对象就放在缓存中。
Spring三级缓存分别是
一级缓存:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
二级缓存:
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存:
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
三级缓存的执行流程在DefaultSingletonBeanRegistry类中的getSingleton方法
先从一级缓存中取,一级缓存总没有再从二级缓存中取,二级缓存总没有再从三级缓存中取
如果发生循环依赖的对象,不需要代理的话,只需要二级缓存足矣;但是如果存在代理之后,必须要使用三级缓存才能解决。
好,接下来自定义类和xml文件进行Debug的跟踪:
来到preInstantiateSingletons方法中
// 将所有BeanDefinition的名字创建一个集合
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 触发所有非延迟加载单例Bean的初始化,遍历集合的对象
for (String beanName : beanNames) {
// 合并父类BeanDefinition
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 需满足:抽象&&单例&&非懒加载
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判断是否实现了FactoryBean接口
if (isFactoryBean(beanName)) {
.....
} else {
// 如果beanName对应的bean不是FactoryBean,而只是普通的Bean
// 通过beanName获取bean实例
getBean(beanName);
}
进入getBean方法中,再进入doGetBean方法中,这个方法中有一个很重要的判断方法getSingleton,这个就是从一级缓存中判断下有没有这个对象
返回到doGetBean方法中
// 当对象都是单例的时候尝试解决循环依赖的问题
// 但是原型模式下如果存在循环依赖的情况
// 则直接抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
代码继续往下走
// 创建Bean的实例对象
if (mbd.isSingleton()) {
// 返回以beanName的原始单例对象,如果尚未注册
// 则使用singletonFactory创建并注册一个对象
sharedInstance = getSingleton(beanName, () -> {
try {
// 为给定的合并后BeanDefinition和参数创建一个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);
}
当调用getSingleton这个方法的时候,第一个参数是beanName,第二个参数是一个匿名内部类,就是一个lambda表达式。进入方法中
进入到singletonFactory.getObject()方法中,继续进入到createBean方法中,找到doCreateBean方法进入
// 根据执行bean使用对应的策略创建新的实例
// 比如:工厂方法、构造函数主注入、简单初始化等
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 从包装类中获取原始Bean
Object bean = instanceWrapper.getWrappedInstance();
此时的A是只是一个完成了实例化,尚未初始化的半成品。
代码继续往下走
// 判断当前Bean是否需要提前曝光:单例&&循环依赖&&当前bean正在创建中,检测循环依赖
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加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
进入addSingletonFactory方法
synchronized (this.singletonObjects) {
// 如果单例对象的高速缓存[beanName-bean实例]中没有beanName对应的这个对象
if (!this.singletonObjects.containsKey(beanName)) {
// 将beanName、singletonFactory放到单例工厂的缓存[beanName-ObjectFactory]中
this.singletonFactories.put(beanName, singletonFactory);
// 从早去单例对象的高速缓存[beanName-bean实例]中移除beanName的相关缓存对象
this.earlySingletonObjects.remove(beanName);
// 将beanName添加已注册的单例集合中
this.registeredSingletons.add(beanName);
}
}
代码继续往下走回到doCreateBean方法中
// 对bean的属性进行填充,将各个属性值注入
// 如果存在依赖其它bean的属性,则会递归初始化依赖的bean
populateBean(beanName, mbd, instanceWrapper);
进入populateBean方法中
// 应用给定的属性值,解决任何在这个bean工厂运行时其它bean的引用
// 必须使用深拷贝,所以我们不会永久的修改这个属性
applyPropertyValues(beanName, mbd, bw, pvs);
进入到applyPropertyValues方法中
for (PropertyValue pv : original) {
.....
} else {
// 获取属性的名字
String propertyName = pv.getName();
// 获取未经类型转换的值
Object originalValue = pv.getValue();
// AutowiredPropertyMarker.INSTANCE:自动生成标记的规范实例
if (originalValue == AutowiredPropertyMarker.INSTANCE) {
.....
}
// 交由valueResolver根据pv解析出originalValue所封装的对象
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
.....
}
进入到resolveValueIfNecessary方法中
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
// 解析出对应ref所封装的Bean元信息[beanName,bean类型]的Bean对象
return resolveReference(argName, ref);
}
进入到resolveReference方法中
resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
// 获取resolvedName的Bean对象,也就是从容器中获取
bean = this.beanFactory.getBean(resolvedName);
进入到getBean方法中,继续进入到doGetBean方法中
// 提前检查单例缓存中是否有手动注册的单例对象
// 跟循环依赖有关联
Object sharedInstance = getSingleton(beanName);
进入到getSingleton方法中,继续进入getSingleton方法中
// 从单例对象缓存中获取beanName对应的单例对象
Object singletonObject = this.singletonObjects.get(beanName);
// 如果单例对象缓存中没有,且该beanName对应的单例bean正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从earlySingletonObjects缓存中获取单例对象
// 获取的这个对象是通过提前曝光的ObjectFactory创建出来的
// 还未进行属性填充等操作
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果earlySingletonObjects缓存中也没有
// 并且也允许创建的话
if (singletonObject == null && allowEarlyReference) {
.....
}
代码继续往下走,回到doGetBean方法中
// 如果不是做类型检查,那么表示要创建bean,此处在结合中做一个记录
if (!typeCheckOnly) {
// 为beanName标记为已经创建,或将要创建
markBeanAsCreated(beanName);
}
代码继续往下走,来到getSingleton方法,继续进入,会发现B也将创建一个半成品对象。然后对B进行属性填充,就需要用到A对象。A对象在之前已经创建出来,有在三级缓存中的任何一级缓存中吗?显然并没有。代码继续往下走,再次来到getSingleton方法中
singletonObject == null为true
allowEarlyReference也为true
刚刚跳过的这段代码开始进入执行
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) {
// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法
// 将对应的ObjectFactory初始化策略存储在singletonFactories
// 这便是从三级缓存中取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 如果存在单例对象工厂,则通过工厂创建一个单例对象
singletonObject = singletonFactory.getObject();
// 记录在缓存中,二级缓存和三级缓存的对象不能同时存在
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
singletonObject = singletonFactory.getObject()点F7进入就会来到addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))这个方法中的lambda表达式() -> getEarlyBeanReference(beanName, mbd, bean)。继续往下执行,回到getSingleton方法中,会发现将半成品的A返回了
代码继续往下走,回到doCreateBean方法中,就会发现B中的a属性已经被A赋值了
代码继续往下走,来到getSingleton方法中
// 这个单例对象是不是新对象
if (newSingleton) {
// 将beanName和singletonObject的映射关系添加到该工厂的单例单例缓存中
addSingleton(beanName, singletonObject);
}
进入addSingleton方法中
synchronized (this.singletonObjects) {
// 放到一级缓存中
this.singletonObjects.put(beanName, singletonObject);
// 从二级缓存中移除
this.singletonFactories.remove(beanName);
// 从三级缓存中移除
this.earlySingletonObjects.remove(beanName);
// 将beanName添加到已注册的单例集中
this.registeredSingletons.add(beanName);
}
此时B对象已经完成,回到A对象的赋值操作中,将B赋值给A对象的a属性。那么对A继续刚刚和B一样的操作,放到一级缓存中,从二级缓存中移除,从三级缓存中移除。这就是完整的一个创建过程。
那么,从上述的创建过程中,三级缓存是怎么流转的呢?
三级缓存中,并不是严格的从三级到二级到一级。有可能在三级和一级中有对象,也有可能再一二三级中都有对象。
如何改变源码中的三级缓存引用关系,将三级缓存改为二级缓存?
1、在doCreateBean中有添加lambda表示式到三级缓存的代码,要改成直接放到二级缓存;
2、直接修改getSingleton方法,将三级缓存去掉;