我是靠谱客的博主 美丽彩虹,最近开发中收集的这篇文章主要介绍01 Spring程序入口和XML解析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

由于md文档部分语法不兼容,建议访问作者网站查阅文章:wlizhi.cc

1 前言


Spring源码Gitee传送门 (本人从 github 中下载,然后以自身理解对核心流程及主要节点做了详细的中文注释。建议下载、结合 Spring 系列文章阅读源码)

spring源码系列文章,示例代码的中文注释,均是copy上面链接中的源码。

用 jack大佬 的话来概括,为什么要学习Spring源码?

  • 提高自己的写代码能力
  • 从全局考虑如何使代码变得灵活可扩展
  • Spring重新定义了Java
  • Spring源码是有一定难度的
  • 如果想成为真正的高手,Spring源码必须攻克,不是危言耸听的
  • Spring源码读懂,其他很多基于Spring的框架源码很容易理解。相反,以Spring为基础的框架、组件,不懂Spring压根没法读其源码。

学习源码的方式:主抓脉络,后看细节。(熟练使用的前提下再看源码

特别感谢:Spring源码中的调用链路、纵深都是比较长的,我个人读了很久。许多源码难点的理解得益于 jack大佬 的讲解,感谢大佬的授业解惑。

2 Spring的历史


  1. 2002年10月,Rod Johnson发布《Expert One-on-One J2EE设计和开发》一书
  2. 2004年3月,Spring1.0发布
  3. 2006年10 月,Spring2.0发布
  4. 2009年12月,Spring3.0发布
  5. 2013年12月,发布Spring4.0
  6. 2017年9月,Spring5.0发布

3 读源码前的准备


  1. JDK1.8版本
  2. spring 5.1.3.RELEASE版本
  3. 补一下Lambda表达式的知识
  4. 安装配置gradle

4 Spring源码下载


  1. git clone --branch v5.1.3.RELEASE https://gitee.com/wanglizhi00/spring-framework (此链接的Spring源码,核心流程、要注意的点我都做了中文注释,非常详细。如果你的网络足够好,也可以到 github 下载 原版spring源码 )
  2. gradle下载,gradle要JDK8的版本
  3. 到下载的spring源码路径执行gradle命令,gradlew :spring-oxm:compileTestJava
  4. 用idea打开spring源码工程。(在idea中安装插件kotlin,重启idea,跟IDEA版本有关较新版本不用安装
  5. 把编译好的源码导入到工程中
  6. 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()

  1. 创建XmlBeanDefinitionReader对象
  2. 通过Reader对象加载配置文件
  3. 根据加载的配置文件把配置文件封装成document对象
  4. 创建BeanDefinitionDocumentReader对象,DocumentReader负责对document对象解析
  5. parseDefaultElement(ele, delegate);负责常规标签解析
  6. delegate.parseCustomElement(ele);负责自定义标签解析
  7. 最终解析的标签封装成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解析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部