《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,每天给你答疑解惑,还能有第二份收入,这样的知识星球,难道你还要犹豫!