mini-spring 学习笔记—高级篇
最近在学习 mini-spring 项目,记录笔记以总结心得
IoC篇:mini-spring 学习笔记—IoC
AOP篇:mini-spring 学习笔记—AOP
解决循环依赖问题(一):没有代理对象
DefaultSingletonBeanRegistry
增加 Map 类型的属性 earlySingletonObjects,用作二级缓存,存放提前实例化(设置属性之前)的 bean
protected Map<String, Object> earlySingletonObjects = new HashMap<>();
getSingleton 方法增加检查二级缓存的代码
// getSingleton 方法
Object bean = singletonObjects.get(beanName);
if (bean == null) {
bean = earlySingletonObjects.get(beanName);
}
return bean;
AbstractAutowireCapableBeanFactory
在 doCreateBean 方法中提前将 bean 放入二级缓存中
// doCreateBean
earlySingletonObjects.put(beanName, bean);
解决循环依赖问题(二):有代理对象
本章是 Spring 解决循环问题的核心,为了方便阅读,首先介绍一下 Spring 三级缓存各自的作用
singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用earlySingletonObjects:提前曝光的单例对象的 cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖singletonFactories:单例对象工厂的 cache,存放 bean 工厂对象,用于解决循环依赖
DefaultSingletonBeanRegistry
增加三级缓存 DefaultSingletonBeanRegistry,用于存放单例 bean 的工厂
private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
从代码可以知道,String 类型代表 bean 的名字,也就是说为每一种 bean 存放一个 bean 工厂
在 getSingleton 方法中增加查询三级缓存的代码
// getSingleton 方法
Object singletonObject = singletonObjects.get(beanName);
if (singletonObject == null) {
// 查询二级缓存
singletonObject = earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 查询三级缓存
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//从三级缓存放进二级缓存
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
}
return singletonObject;
singletonFactories.get(beanName) 通过 bean 名称获取制造该 bean 的工厂,然后让工厂制造该 bean 并返回
AbstractAutowireCapableBeanFactory
getObject 方法的具体实现在该类的 doCreateBean 方法中
// doCreateBean 方法
// 为解决循环依赖问题,将实例化后的bean放进缓存中提前暴露
if (beanDefinition.isSingleton()) {
Object finalBean = bean;
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, beanDefinition, finalBean);
}
});
}
getEarlyBeanReference 方法用于返回 bean 的代理对象
protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
Object exposedObject = bean;
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
// 获得代理对象
// 此时代理对象在 earlyProxyReferences 中
exposedObject = ((InstantiationAwareBeanPostProcessor) bp).getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return exposedObject;
}
}
}
return exposedObject;
}
比如在本章的测试中,传入了 bean a,那么在实例化 a 的过程中,这个方法会产生如下的运行结果

而 exposedObject 的具体字段内容如下

可以看到 exposedObject 中 target 成员的自我描述为 A@1224 和传入的 bean 变量自我描述相同,所以 exposedObject 是 a 的代理对象
DefaultAdvisorAutoProxyCreator
增加 Set 类型的成员变量 earlyProxyReferences,用于保存提前实例化且未被代理包裹的 bean
private Set<Object> earlyProxyReferences = new HashSet<>();
关于原文对于 getBean 方法的描述
原文在这一章对于 getBean 方法的描述有歧义
getBean()时依次检查一级缓存singletonObjects、二级缓存earlySingletonObjects和三级缓存singletonFactories中是否包含该bean。如果三级缓存中包含该 bean,则挪至二级缓存中,然后直接返回该 bean。见AbstractBeanFactory#getBean方法第1行。
定位到 AbstractBeanFactory 类的 getBean 方法的第一行
Object sharedInstance = getSingleton(name);
调用的是 getSingleton 方法,而在上文的分析中, getSingleton 方法对第三级缓存的操作是让 singletonObject 的工厂来制造一个 singletonObject 以获取 singletonObject。也就是说,原文“如果三级缓存中包含该 bean,则挪至二级缓存中,然后直接返回该 bean”中“三级缓存包含该 bean” 指的是三级缓存能够制造该 bean。
本章小结
详细步骤
此时梳理一下循环依赖情况下的 bean 创建过程
在 AbstractApplicationContext 类的 finishBeanFactoryInitialization 方法打断点

preInstantiateSingletons 方法对 bean 定义容器中的每个定义调用 getBean 方法,此时容器中有六个 bean 定义

首先获取 a

进入 getSingleton 方法,查询完三级缓存之后,发现没有制造 a 的工厂,该方法返回 null

回到 getBean 方法中,调用 createBean 方法,之后调用 doCreateBean 方法

在 doCreateBean 方法中获取实例化后的 a,判断如果是单例 bean,则进入 addSingletonFactory 方法,在三级缓存中添加该 bean 的制造工厂,添加之后三级缓存 size = 1

之后返回 doCreateBean 方法中,进行填充属性

发现 a 依赖于 b,对 b 进行 getBean 操作

同样进入 getSingleton 方法

查询完三级缓存,发现没有 b 的工厂

为 b 调用 createBean

同样进入到 doCreateBean 方法,再调用 addsingletonFactory 方法为 b 添加制造工厂,此时三级缓存 size = 2

为 b 填充属性,发现依赖于 a,尝试对 a 进行 getBean 操作

进入 getSingleton 方法

此时发现三级缓存中有制造 a 的工厂,便让该工厂使用 getObject 方法制造 a
进入 getObject 方法,调用 getEarlyBeanReference

调用 getEarlyBeanReference 方法,获取 a 的代理对象

在 getEarlyBeanReference 方法中,先往 earlyProxyReferences 集合中添加 bean 名称

之后进入 wrapIfNecessary 方法,为 a 设置代理



回到 getEarlyBeanReference 方法,此时的 exposedObject 对象是 a 的 CGLIB 代理

回到 getSingleton 方法,此时 singletonObject 对象是 a 的代理对象,并把 a 的工厂从三级缓存中删除,然后把 a 的代理对象 singletonObject 放入二级缓存中

将 a 添加到二级缓存,并把 a 的工厂从三级缓存中删除
为 b 设置属性 a

为 b 获取代理对象,调用 getSingleton 方法

此时三级缓存中已经有了 b 的工厂

依然调用 getObject 方法,进入 getEarlyBeanReference 方法,但是返回的是非代理对象

对三级缓存和二级缓存进行操作后

回到 doCreateBean 方法,对 b 调用 addSingleton 方法,将初始化好的 b 放入一级缓存,从二级缓存中删除 b


回到 b 的 getBean 方法,再返回到 applyPropertyValues,给 a 设置 b 属性

再次回到 doCreateBean 方法中,获取 a 的代理对象

再次进入 getSingleton 方法,此时已经能够在二级缓存中找到 a

进入 addSingleton 方法,此时 exposedObject 已经是代理对象了



此时所有的 bean 都在一级缓存中了

太长不看总结版
总的来说,当我们要实例化 a 的时候,会经历以下步骤:
- 实例化
a,将a工厂放入三级缓存中 - 为
a设置属性时发现依赖于b - 实例化
b,将b工厂放入三级缓存中 - 为
b设置属性时发现依赖于a,且三级缓存中有a工厂 - 从
a工厂中获取a的代理对象,并将其放入二级缓存,删除三级缓存中的a工厂,并返回a - 设置
b的属性 - 获取
b的代理对象,将b的代理对象放入二级缓存,删除三级缓存中的b工厂 - 将
b的代理对象放入一级缓存,从二级缓存中删除b的代理对象,并返回b - 通过反射为最开始的
a设置属性b,此时二级缓存中的a也具有了相同的b属性(因为缓存中的a和最初的a是同一个对象) - 从二级缓存中能找到
a的代理对象并返回 - 将
a的代理对象放入一级缓存,并从二级缓存中删除
此时会有一个疑问:为什么缓存中的 a 和最初的 a 是同一个对象?因为在 b 调用 getObject 函数的时候,getObject 中 getEarlyBeanReference 函数的 beanDefinition 参数值和 finalBean 参数值在添加进三级缓存的时候已经固定好了,所以 finalBean 指向的其实是第一个 a
支持懒加载和多切面增强
这一章下作者合并了多个提交,但主要改动在“增加懒加载,重写了 AOP”提交记录
懒加载
BeanDefinition
增加 Boolean 类型的成员变量 lazyInit,用于标记该 bean 是否懒加载
private boolean lazyInit=false;
DefaultListableBeanFactory
preInstantiateSingletons 方法添加了关于 bean 是否是懒加载的判断
public void preInstantiateSingletons() throws BeansException {
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
if(beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){
getBean(beanName);
}
});
}
多个切面匹配同一个方法
ProxyFactory
这一节讲的挺清楚的,就不再赘述
基于JDK动态代理
这一节讲的挺清楚的,就不再赘述

浙公网安备 33010602011771号