共计 13957 个字符,预计需要花费 35 分钟才能阅读完成。
《Spring 源码解读》
傻瓜源码 - 内容简介
傻瓜源码 - 内容简介 |
---|
????【职场经验】(持续更新) 精编短文:如何成为值钱的 Java 开发 - 指南 如何日常学习、如何书写简历、引导面试官、系统准备面试、选择 offer、提高绩效、晋升 TeamLeader….. |
????【源码解读】(持续更新) <br/>1. 源码选材:Java 架构师必须掌握的所有框架和类库源码<br/>2. 内容大纲:按照“企业应用 Demo”讲解执行源码:总纲“阅读指南”、第一章“源码基础”、第二章“相关 Java 基础”、第三章“白话讲源码”、第四章“代码解读”、第五章“设计模式”、第六章“附录 - 面试习题、相关 JDK 方法、中文注释可运行源码项目” 3. 读后问题:粉丝群答疑解惑 |
已收录:HashMap、ReentrantLock、ThreadPoolExecutor、《Spring 源码解读》、《Dubbo 源码解读》….. |
????【面试题集】(持续更新)<br/>1. 面试题选材:Java 面试常问的所有面试题和必会知识点 <br/>2. 内容大纲:第一部分”注意事项“、第二部分“面试题解读”(包括:”面试题“、”答案“、”答案详解“、“实际开发解说”) 3. 深度 / 广度:面试题集中的答案和答案详解,都是对齐一般面试要求的深度和广度 4. 读后问题:粉丝群答疑解惑 |
已收录:Java 基础面试题集、Java 并发面试题集、JVM 面试题集、数据库 (Mysql) 面试题集、缓存 (Redis) 面试题集 ….. |
????【粉丝群】(持续更新) <br/>收录:阿里、字节跳动、京东、小米、美团、哔哩哔哩等大厂内推 |
???? 作者介绍:Spring 系源码贡献者、世界五百强互联网公司、TeamLeader、Github 开源产品作者 ???? 作者微信:wowangle03(企业内推联系我) |
加入我的粉丝社群,阅读更多内容。从学习到面试,从面试到工作,从 coder 到 TeamLeader,每天给你答疑解惑,还能有第二份收入!
第 1 章 阅读指南
- 本书基于 Spring 5.0.x(5.0.16.BUILD-SNAPSHOT)版本。
- 本书根据”企业应用 Demo“解读源码。
-
本书建议分为两个学习阶段,掌握了第一阶段,再进行第二阶段;
- 第一阶段,理解章节“源码解读”前的所有内容。即掌握 IT 技能:熟悉 Spring 原理。
- 第二阶段,理解章节“源码解读”(包括源码解读)之后的内容。即掌握 IT 技能:精读 Spring 源码。
- 建议按照本书内容顺序阅读(内容前后顺序存在依赖关系)。
- 阅读过程中,如果遇到问题,记下来,后面不远的地方肯定有解答。
- 阅读章节“源码解读”时,建议获得中文注释源码项目配合本书,Debug 进行阅读学习。
-
源码项目中的注释含义;
- ”企业应用 Demo“在源码中,会标注“// Spring Demo”。
- 在源码中的不易定位到的主线源码,会标注“// tofix 主线”。
-
以下注释的源码,暂时不深入讲解:
- 在执行“企业应用 Demo”过程中,没有执行到的源码(由于遍历空集合、if 判断),会标注“/* Demo 不涉及 /”。
- 在执行”企业应用 Demo“过程中,有用变量的数据转换方法,输入值和输出值相同(由于遍历空集合、if 判断没有处理数据),会标注“/* 无效果 /”。
- 从头到尾都是空的变量(包括不包含元素的集合),会标注“/* 空变量 /”。
- 有被赋值的变量,但“企业应用 Demo”运行过程中没有使用到该变量,会标注”/* 无用逻辑 /“。
- 不是核心逻辑,并且不影响源码理解,会标注”/* 非主要逻辑 /“。
- 锁、异常处理逻辑、非空校验、日志打印没有标注注释。
第 2 章 Spring 实战
2.1 源码本地构建
- 下载作者详细中文注释后的 Spring 源码;
- 保证本地已经安装 jdk(最好 1.8)、gradle(最好 4.4.1 版本);
- 进入项目根目录,打开 cmd 或者 git 命令行,输入 ./gradlew :spring-oxm:compileTestJava 进行编译,如果中途编译失败,就重复编译几次;
- 然后使用 Idea 导入项目,使用 gradle 进行 import;
- 全局搜索“// Spring Demo”,运行“企业应用 Demo”。
2.2 基础入门 Demo
代码示例 1
public class PersionA {
private PersionB pb;
public PersionB getPb() {return pb;}
public void setPb(PersionB pb) {this.pb = pb;}
}
代码示例 2
public class PersionB {
private PersionA pa;
public PersionA getPa() {return pa;}
public void setPa(PersionA pa) {this.pa = pa;}
}
代码示例 3 application.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
>
<bean id="persionA" class="org.springframework.demo.PersionA">
<property name="pb" ref="persionB"/>
</bean>
<bean id="persionB" class="org.springframework.demo.PersionB">
<property name="pa" ref="persionA"/>
</bean>
</beans>
代码示例 4 启动 Spring
public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
PersionA persion = (PersionA) context.getBean("persionA");
System.out.println(persion.getPb());
}
}
2.3 企业应用 Demo
暂时由“基础入门 Demo”代替,”企业应用 Demo“以及配套的第 2 版《Spring 源码解读》正在修改中 …。
第 3 章 相关 Java 基础
3.1 实例化接口
代码示例 1 接口
public interface Person {public abstract void eat();
}
代码示例 2 实例化接口
public class Demo {public static void main(String[] args) {
// 使用 Lambda 直接创建 Persion 接口实例
Person p1 = () -> System.out.println("eat something!");
// 打印结果:eat something!p1.eat();}
}
直接实例化接口适用于在多个不同的调用场合,抽象方法会有不同实现逻辑的场景。反过来想,如果不使用 Lambda 创建 Persion 接口实例,想要在不同调用场合,执行不同实现逻辑,就必须为每个场合定义一个实现了接口的类,然后在实现类的方法里实现对应逻辑。这样比较,使用 Lambda 表达式直接创建接口实例是不是大大简化了代码呢!
第 4 章 源码基础
4.1 导读
1. Spring 中的对象种类
在”企业应用 Demo“中,有涉及到两种对象:Pojo、Bo;
- Pojo(plian ordinary java object):仅包含属性以及属性的 get、set、add、remove、is、has 方法的对象;
- Bo(business object):就是封装着业务逻辑的对象。
4.2 ClassPathXmlApplicationContext
ClassPathXmlApplicationContext(Bo),继承自 AbstractRefreshableConfigApplicationContext,调用构造函数实例化的同时,启动了 Spring;在“企业应用 Demo”中主要负责创建 DefaultListableBeanFactory 工厂对象和 XmlBeanDefinitionReader 对象来执行逻辑。
代码示例 重要成员变量
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
// 继承自 AbstractRefreshableConfigApplicationContext
// configLocations 表示 application.xml 配置文件的路径,可以配置多个配置文件,例:["classpath*:applicationContext.xml"]
private String[] configLocations;
// 继承自 AbstractRefreshableApplicationContext
// beanFactory 表示 DefaultListableBeanFactory 实例,总的来说就是用于生成 Bean 的工厂。比如:生成目标对象(例:PersionA)。private DefaultListableBeanFactory beanFactory;
4.3 XmlBeanDefinitionReader
XmlBeanDefinitionReader(Bo),简称:XmlBean 定义读取器;负责读取 application.xml 配置文件,解析成 Resource 实例(Spring 定义的,是用来封装文件资源的类),再根据 Resource 实例获取到的 inputStream 输入流,转化成 Document 实例;然后交由 DefaultBeanDefinitionDocumentReader 对象进行下一步操作。
代码示例 重要成员变量
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
// resourceLoader 表示 ClassPathXmlApplicationContext 实例,负责读取 applicaiton.xml 文件为 Resource 实例
private ResourceLoader resourceLoader;
// documentLoader 负责将代表 Resource 的 inputStream 输入流转换成 Document 对象
private DocumentLoader documentLoader = new DefaultDocumentLoader();
// resourcesCurrentlyBeingLoaded 保存当前线程加载了的 application.xml 配置文件(包括 <import/> 标签引进的 xml 资源),用来检查 <import/> 造成的循环导入
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
new NamedThreadLocal<>("XML bean definition resources currently being loaded");
// 继承自 AbstractBeanDefinitionReader
// registry 表示 DefaultListableBeanFactory 实例,XmlBeanDefinitionReader 是通过构造函数获取到并持有的这个对象,是为了向后续逻辑传递下去
private final BeanDefinitionRegistry registry;
4.4 DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader(Bo),简称:Bean 定义 Document 读取器;负责读取 Document 实例中的节点信息,如:< bean/>、< import/> 等;然后交由 BeanDefinitionParserDelegate 对象进行下一步操作。
代码示例 重要成员变量
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
// readerContext 表示 XmlReaderContext 实例,Xml 读取器上下文
// 作用:为了将 XmlBeanDefinitionReader 实例、Resource 实例、DefaultListableBeanFactory 实例等 统一封装到 XmlReaderContext 里,向后传递使用
private XmlReaderContext readerContext;
// delegate 代表 BeanDefinitionParserDelegate 实例;负责将 Document 对象(例如:< bean/>)装载为 GenericBeanDefinition
private BeanDefinitionParserDelegate delegate;
4.5 BeanDefinitionParserDelegate
BeanDefinitionParserDelegate(Bo),简称:Bean 定义解析代理;负责将 Document 对象(例如:< bean/>)装载成 GenericBeanDefinition 对象;然后交由 DefaultListableBeanFactory 对象进行下一步操作。
代码示例 重要成员变量
public class BeanDefinitionParserDelegate {
// readerContext 表示 XmlReaderContext 实例,上文提过,是 Xml 读取器上下文
// 用于从 XmlReaderContext 里获取 DefaultListableBeanFactory 实例等对象)private final XmlReaderContext readerContext;
4.6 GenericBeanDefinition
GenericBeanDefinition(Pojo),简称:通用 Bean 定义;是用来装载 < bean/> 配置的实体类。
代码示例 重要成员变量
public class GenericBeanDefinition extends AbstractBeanDefinition {
// beanClass 对应 <bean class=""/> 中的 class 值,一开始会被赋值为 class 设置的字符串,后面会被赋值为解析后的 Class 对象
private volatile Object beanClass;
// 继承自 AbstractBeanDefinition
// propertyValues 是保存 <property/> 信息的实体类
private MutablePropertyValues propertyValues;
4.7 BeanDefinitionHolder
BeanDefinitionHolder(Pojo),简称:Bean 定义持有者;负责持有 GenericBeanDefinition 对象(也就是说 GenericBeanDefinition 对象是 BeanDefinitionHolder 一个成员变量),在“企业应用 Demo”中,只起到数据传输的作用。
BeanDefinitionHolder 的必要性和 BeanNameAware 相关;”企业应用 Demo“不涉及。
代码示例 重要成员变量
public class BeanDefinitionHolder implements BeanMetadataElement {
// beanDefinition 表示 GenericBeanDefinition 实例;用于装载从 application.xml 配置文件中解析出来的 < bean/>
private final BeanDefinition beanDefinition;
// beanName 表示 <bean/> 在 Spring 中的名字,一般为 <bean id=""/> 中的 id 值
private final String beanName;
4.8 DefaultListableBeanFactory
DefaultListableBeanFactory(Bo);总的来说就是生成 Bean 的工厂。在”企业应用 Demo“中,主要负责根据 GenericBeanDefinition 对象,生成目标对象(例:PersionA)并注入属性值,放到 Map<String, Object> singletonObjects 里保存起来,供应用程序使用。
代码示例 重要成员变量
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
// beanDefinitionNames 用于存放从 application.xml 文件中解析出来的 beanName(一般为 <bean/> 标签中的 id 值),按注册顺序排列
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
// 数据结构:{beanName -> GenericBeanDefinition 对象}, 用于后续逻辑根据 beanName 获取 GenericBeanDefinition 实例来用
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 继承自 AbstractBeanFactory
// 数据结构:{beanName -> RootBeanDefinition 实例} 缓存, 用于防止重复创建 beanName 的 RootBeanDefinition
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
// 继承自 DefaultSingletonBeanRegistry
// singletonsCurrentlyInDestruction 用于标记 Spring 是否处在销毁单例的过程,默认为 false;如果设置为 true,创建单例 bean 的时候,就会抛出 BeanCreationNotAllowedException 异常
private boolean singletonsCurrentlyInDestruction = false;
/**
*
* 以下成员变量,参考下一节“循环依赖”,进行理解
*
*/
// 继承自 DefaultSingletonBeanRegistry
// singletonsCurrentlyInCreation 中存储的 beanName 都是处于创建过程中的
// 当该目标对象创建完成后,会将对应的 beanName 从 singletonsCurrentlyInCreation 集合中剔除掉
// 作用:主要用于解决 IOC 循环依赖的问题
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
// 继承自 DefaultSingletonBeanRegistry
// 数据结构:{beanName -> 单例 bean 对象(例:PersionA 实例)},也被称为一级缓存;// 添加场景:在“企业应用 Demo”中,当 application.xml 中定义的 bean 对象被完全实例化后(完全实例化是指实例化并注入属性值后),则会被放入本缓存中
// 作用:单例 bean 会被放到这个缓存里,当应用系统想要得到 Spring 管理的 <bean/> 时(例:context.getBean("persion");),就可以直接从这个缓存里获取
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 继承自 DefaultSingletonBeanRegistry
// 数据结构:{beanName —> ObjectFactory 接口的实现类实例} , 也称为三级缓存
// ObjectFactory 是生产目标对象的工厂接口;不同场景下,生成对象的逻辑不同,所以这里使用了工厂接口(比如:生成 AOP 代理对象和通过构造函数生成普通对象等)// 添加场景:在“企业应用 Demo”中,当 bean 对象(例:PersionA 实例)实例化后,没有注入属性值之前,会放入本缓存;// 移除场景:在“企业应用 Demo”中,当 bean 对象完全实例化后,则会从本缓存中剔除掉
// 作用:在“企业应用 Demo”中,singletonObjects 存放的 value 值是 lambda 表达式创建的 ObjectFactory 接口实例;// singletonFactories 是创建目标对象过程中,用于存放处于中状态对象的临时容器
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 继承自 DefaultSingletonBeanRegistry
// 数据结构:{beanName -> bean 对象(例:PersionA 实例)},是提前曝光的单例对象缓存,也称为二级缓存;// 添加场景:在“企业应用 Demo”中,当 bean 对象(例:PersionA 实例)实例化后,发现成员变量指定了其它 Spring 管理的 <bean/> 对象(例:PersionB 实例),并且这个 <bean/> 对象(例:PersionB 实例)存在于 singletonsCurrentlyInCreation,也存在于三级缓存中,就会把 PersionB 对象从三级缓存中移除,放到二级缓存里;(如果未发生过循环依赖的场景,二级缓存从始至终没有存在过值)// 移除场景:在“企业应用 Demo”中,当 bean 对象完全实例化后,则会从本缓存中剔除掉
// 作用:用于解决 IOC 循环依赖的问题
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
4.9 循环依赖
1. 问题
如果 persionA 依赖 persionB,persionB 依赖 persionA,Spring 是如何初始化 PersionA 和 PersionB 的?(这个现象称之为”循环依赖“)
2. 答案
persionA | persionB |
---|---|
1. 要开始创建 persionA 对象之前,向 singletonsCurrentlyInCreation 添加该对象的对应 beanName(旨在标记 persionA 处于’创建中‘) | |
2. 创建完 persionA 对象后,再把 persionA 对象存在三级缓存中,再准备开始注入属性 | |
3. 注入属性时,发现依赖 persionB,要创建 persionB 对象 | |
总结:这时,persionA 对象的 pb 属性为空;三级缓存包含 persionA | |
1. 要开始创建 persionB 对象之前,向 singletonsCurrentlyInCreation 添加该对象的对应 beanName(旨在标记 persionB 处于’创建中‘) | |
2. 创建完 psersionB 对象后,再把 persionB 对象存在三级缓存里,再准备开始注入属性 | |
2. 注入属性时,发现依赖 persionA 对象 | |
3. 创建 persionA,发现 singletonsCurrentlyInCreation 存有相应 beanName(PersionA 处于 ’ 创建中 ’),并且保存在三级缓存里,则直接从三级缓存中移除 PersionA 对象,然后放到二级缓存里 | |
4. 将 persionA 对象注入到 persionB 对象的 pa 属性里 | |
5. 从三级缓存中删掉 persionB 对象,放到一级缓存里 | |
总结:这时,persionB 对象的 pa 属性不为空,但是 pa 属性 (persionA) 的 pb 属性为空;二级缓存包含 persionA,一级缓存包含 persionB | |
4. 获得 persionB 对象,放到 pb 属性里 | |
5. 从二级缓存中删掉 persionA 对象,放到一级缓存里 | |
总结:这时,persionA 对象的 pb 属性不为空,pb 属性的 pa 属性也不为空;一级缓存包含 persionA 和 persionB |
4.10 RootBeanDefinition
RootBeanDefinition(Pojo);< bean/> 有继承的能力(< bean parent=””/>),所以 Spring 会进行对父子 < bean/> 进行合并操作,最后合并成 RootBeanDefinition 实例,区别于 GenericBeanDefinition 实例。
代码示例 重要成员变量
public class RootBeanDefinition extends AbstractBeanDefinition {
// beanClass 表示 <bean class="org.springframework.PersionA"/> 中的 class 值,一开始为 "org.springframework.PersionA" 字符串,然后会解析为 PersionA Class 对象,再 set 到当前属性里
private volatile Object beanClass;
// 继承自 AbstractBeanDefinition
// propertyValues 表示对应 < property/> 信息的实体类
private MutablePropertyValues propertyValues;
// scope 表示 <bean/> 的单例模式;scope 默认为 "",当 Spring 发现值是空字符串时,并且用户没有指定,就会将值修改为"singleton"(单例),也就是说 Spring 的 <bean/> 默认是单例的。private String scope = "";
4.11 MutablePropertyValues
MutablePropertyValues(Pojo),在 Spring 中,用于封装 < property/> 信息的实体类。
代码示例 重要成员变量
public class MutablePropertyValues implements PropertyValues, Serializable {
// propertyValueList 表示 <property/> 集合(集合中的一个元素对应一个 <property/> 标签)private final List<PropertyValue> propertyValueList;
4.12 PropertyValue
PropertyValue(Pojo),在 Spring 中,对应 < property/> 的实体类,保存了 < property/> 的配置信息,比如属性名、属性值等;这里 Spring 之所以不使用 Map 这种键值对类,是因为自定义类有更强的扩展性。
代码示例 重要成员变量
public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {
// name 表示 <property/> 中的 name 属性
private final String name;
// value 表示 <property/> 指定的值;例:<property name="persionB" ref="pB">,value 一开始为指代 ref 的 RuntimeBeanReference 对象;后续逻辑将 RuntimeBeanReference 对象解析为 PersionB 对象,重新覆盖到 value 属性上
private final Object value;
// source 是 PropertyValue 实例;在将 RuntimeBeanReference 对象解析为 PersionB 对象之前和之后,分别会使用两个 PropertyValue 对象去装载,这个 source 属性就是之前的 PropertyValue 对象(例:<property name="persionB" ref="pB">)private Object source;
// conversionNecessary 表示是否转换 <Property/> 指定的值,默认为空(表示有必要进行转换),Spring 就会查找用户自定义的转换器进行转换,当发现没有转换器,就会将 conversionNecessary 设置为 false,不需要转换
volatile Boolean conversionNecessary;
4.13 RuntimeBeanReference
RuntimeBeanReference(Pojo),在 Spring 中,用于装载 < property ref=””/> 中 ref 指定的值。
代码示例 重要成员变量
public class RuntimeBeanReference implements BeanReference {
// beanName 表示 ref 的值
private final String beanName;
4.14 BeanWrapperImpl
BeanWrapperImpl(Bo),持有目标对象(例:PersionA 对象),可以对其设置 / 获取属性的描述信息,比如:查询只读 / 可写属性等。”企业应用 Demo”中,主要负责给 < bean/> 实例化后的目标对象(例:PersionA 对象)注入的 < property/> 所配置的值。
代码示例 重要成员变量
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
// 继承自 AbstractNestablePropertyAccessor
// wrappedObject 表示 <bean/> 所代表的目标对象,例:PersionA 对象
Object wrappedObject;
// 私有类
// BeanPropertyHandler 表示通用属性描述器,用于保存 < property/> 中的相关信息(例:属性所属类 Class、属性对应 get/set 方法 Method 对象等);”企业应用 Demo”中,主要用于从本对象获取相应属性的 set 方法 Method 对象
private class BeanPropertyHandler extends PropertyHandler{...}
4.15 BeanWrapperImpl$BeanPropertyHandler
BeanWrapperImpl$BeanPropertyHandler(Bo)($ 表示 BeanPropertyHandler 是 BeanWrapperImpl 中的内部类),Bean 属性处理器,负责管理 < property/> 属性;”企业应用 Demo”中,主要负责利用反射,调用 setter 方法,对 < bean/> 设置 < property /> 指定的值。
代码示例 重要成员变量
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
private class BeanPropertyHandler extends PropertyHandler {
// GenericTypeAwarePropertyDescriptor 实例,继承自 PropertyDescriptor(来自第三方 jar 包),可以获得类的信息,比 setter 方法、setter 方法的参数类型等
private final PropertyDescriptor pd;
4.16 GenericTypeAwarePropertyDescriptor
GenericTypeAwarePropertyDescriptor(Pojo),通用属性描述器,用于保存 < property/> 中的相关信息(例:属性所属类 Class、属性的对应 get/set 方法 Method 对象等);”企业应用 Demo”中,主要用于从本对象获取相应属性的 set 方法 Method 对象。
代码示例 重要成员变量
final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
// beanClass 表示 <property/> 的所属类的 Class 对象(例:PersionA.Class)private final Class<?> beanClass;
// 继承自 PropertyDescriptor(第三方 jar)// writeMethodRef 记录了 <bean/> 对象(例:PersionA)的 set<property/> 的 Method 对象,用于获取 set 方法反射设置属性值
private final MethodRef writeMethodRef = new MethodRef();
// writeMethod 表示 setter 方法的 Method 对象
private final Method writeMethod;
// propertyType 表示 <property/> 指代值的 Class 对象
private Class<?> propertyType;
<br/>
加入我的粉丝社群,阅读全部内容
从学习到面试,从面试到工作,从 coder 到 TeamLeader,每天给你答疑解惑,还能有第二份收入,这样的知识星球,难道你还要犹豫!