概述
由于md文档部分语法不兼容,建议访问作者网站查阅文章:wlizhi.cc
1 前言
Spring源码Gitee传送门 (本人从 github 中下载,然后以自身理解对核心流程及主要节点做了详细的中文注释。建议下载、结合 Spring 系列文章阅读源码)
spring源码系列文章,示例代码的中文注释,均是copy上面链接中的源码。
用 jack大佬 的话来概括,为什么要学习Spring源码?
- 提高自己的写代码能力
- 从全局考虑如何使代码变得灵活可扩展
- Spring重新定义了Java
- Spring源码是有一定难度的
- 如果想成为真正的高手,Spring源码必须攻克,不是危言耸听的
- Spring源码读懂,其他很多基于Spring的框架源码很容易理解。相反,以Spring为基础的框架、组件,不懂Spring压根没法读其源码。
学习源码的方式:主抓脉络,后看细节。(熟练使用的前提下再看源码
)
特别感谢:Spring源码中的调用链路、纵深都是比较长的,我个人读了很久。许多源码难点的理解得益于 jack大佬 的讲解,感谢大佬的授业解惑。
2 Spring的历史
- 2002年10月,Rod Johnson发布《Expert One-on-One J2EE设计和开发》一书
- 2004年3月,Spring1.0发布
- 2006年10 月,Spring2.0发布
- 2009年12月,Spring3.0发布
- 2013年12月,发布Spring4.0
- 2017年9月,Spring5.0发布
3 读源码前的准备
- JDK1.8版本
- spring 5.1.3.RELEASE版本
- 补一下Lambda表达式的知识
- 安装配置gradle
4 Spring源码下载
git clone --branch v5.1.3.RELEASE https://gitee.com/wanglizhi00/spring-framework
(此链接的Spring源码,核心流程、要注意的点我都做了中文注释,非常详细。如果你的网络足够好,也可以到 github 下载 原版spring源码 )- gradle下载,gradle要JDK8的版本
- 到下载的spring源码路径执行gradle命令,gradlew :spring-oxm:compileTestJava
- 用idea打开spring源码工程。(
在idea中安装插件kotlin,重启idea,跟IDEA版本有关较新版本不用安装) - 把编译好的源码导入到工程中
- spring工程如何搭建这里不做介绍,如何将down下来的源码导入到自己的spring工程依赖库, 我的Gitee-Spring源码注释 中有详细的介绍
5 入口方法
容器:AbstractApplicationContext 抽象父类,核心方法 refresh()。
- ClassPathXmlApplicationContext XML方式启动。
- AnnotationConfigWebApplicationContext 注解方式启动,对局部代码进行测试时比较好用。
- AnnotationConfigServletWebServerApplicationContext SpringBoot启动默认使用的上下文类。
我们知道,spring容器的启动是通过构造ApplicationContext的一个实例开始的。以ClassPathXmlApplicationContext为例。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A61adXxz-1606788036958)(https://oss.wlizhi.cc/blog/spring/ClassPathXmlApplicationContext.png ‘ClassPathXmlApplicationContext UML图’)]
6 Xml解析流程概览
首先进入spring容器启动的核心方法:refresh() ,创建BeanFactory对象 obtainFreshBeanFactory()
- 创建XmlBeanDefinitionReader对象
- 通过Reader对象加载配置文件
- 根据加载的配置文件把配置文件封装成document对象
- 创建BeanDefinitionDocumentReader对象,DocumentReader负责对document对象解析
- parseDefaultElement(ele, delegate);负责常规标签解析
- delegate.parseCustomElement(ele);负责自定义标签解析
- 最终解析的标签封装成BeanDefinition并缓存到容器中
7 创建XmlBeanDefinitionReader对象
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建BeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// TODO 关键点:加载BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
}
在loadBeanDefinitions方法的第一行,创建了XmlBeanDefinitionReader对象。这个类的主要作用是读取BeanDefinition,并将XML文档的读取委托给BeanDefinitionDocumentReader。
8 创建BeanDefinitionDocumentReader对象
来到loadBeanDefinitions(beanDefinitionReader);这里将要读取的xml路径传递进来,循环xml路径,依次进行解析。
注:看注释中的关键点
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
// 首先进入这个方法
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
// 一次解析配置文件,加载配置文件中定义的Bean。
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
}
路径最终会被封装为IinputSource对象,使用委托模式,交给BeanDefinitionDocumentReader处理
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
// 第一步:入口
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 解析xml配置文件,获取到Document对象
Document doc = doLoadDocument(inputSource, resource);
// 解析、注册BeanDefinition
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
//此处分别捕获了很多种异常,省略了。
} catch (Exception ex) {
throw ex;
}
}
// 第二步 创建BeanDefinitionDocumentReader,委托给BeanDefinitionDocumentReader进行解析工作。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 委托模式,将Document的解析及BeanDefinition的注册交给BeanDefinitionDocumentReader
// 这里使用的DefaultBeanDefinitionDocumentReader进行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// TODO 关键点:解析xml、注册BeanDefinition
documentReader.
最后
以上就是美丽彩虹为你收集整理的01 Spring程序入口和XML解析的全部内容,希望文章能够帮你解决01 Spring程序入口和XML解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复