mini-spring 学习笔记—高级篇

最近在学习 mini-spring 项目,记录笔记以总结心得

IoC篇:mini-spring 学习笔记—IoC

AOP篇:mini-spring 学习笔记—AOP

扩展篇:mini-spring 学习笔记—扩展篇

解决循环依赖问题(一):没有代理对象

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 的过程中,这个方法会产生如下的运行结果

image

exposedObject 的具体字段内容如下

image

可以看到 exposedObjecttarget 成员的自我描述为 A@1224 和传入的 bean 变量自我描述相同,所以 exposedObjecta 的代理对象

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 方法打断点

image

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

image

首先获取 a

image

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

image

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

image

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

image

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

image

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

image

同样进入 getSingleton 方法

image

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

image

b 调用 createBean

image

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

image

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

image

进入 getSingleton 方法

image

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

进入 getObject 方法,调用 getEarlyBeanReference

image

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

image

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

image

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

image

image

image

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

image

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

image

a 添加到二级缓存,并把 a 的工厂从三级缓存中删除

b 设置属性 a

image

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

image

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

image

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

image

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

image

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

image

image

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

image

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

image

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

image

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

image

image

image

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

image

太长不看总结版

总的来说,当我们要实例化 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 函数的时候,getObjectgetEarlyBeanReference 函数的 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动态代理

这一节讲的挺清楚的,就不再赘述

posted @ 2023-12-09 12:27  Frodo1124  阅读(41)  评论(0编辑  收藏  举报