Spring 加载、解析applicationContext.xml 流程

2023-11-06

概要

Spring 框架使用了BeanFactory 进行加载 xml 和生成 bean 实例。下面我们分析下Spring加载xml文件的过程。
spring 版本是最新的 4.3.9 release 版本

示例

XmlBeanFactory xbf = new XmlBeanFactory(new ClassPathResource("bean.xml"));
User user = User.class.cast(xbf.getBean("user"));
System.out.println(user);

我们通过XmlBeanFactory分析下xml的加载过程。通常我们开发的时候一般都是使用ClassPathXmlApplicationContext进行加载配置文件的。原理都一样,只不过ClassPathXmlApplicationContext宽展了好多功能。但加载xml的原理都一样。

ClassPathResource 封装了xml文件信息,可以调用getInputStream() 方法获取文件。

源码解析

XmlBeanFactory.java


从代码中发现XmlBeanFactory委托给XmlBeanDefintionReader进行处理

XmlBeanDefintionReader.java


1. 使用EncodeResource封装资源文件。如果指定编码则使用指定编码进行读取资源文件。
2. 判断该资源是否已经加载过
3. 构造InputStream实例,然后调用 doLoadBeanDefinitions() 方法

InputSource 类结构
public class InputSource {
    private String publicId;
    private String systemId;
    private InputStream byteStream;
    private String encoding;
    private Reader characterStream;
    ....
}

使用SAX解析、验证xml的时候需要使用到 publicId和systemId

doLoadBeanDefinitions() 方法


1. 使用SAX解析xml获取Document对象
2. 根据返回的Document 注册 Bean 信息

doLoadDocument()
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware());
}
getValidationModeForResource(resource)


判断xml的文档验证机制是DTD还是XSD
1.如果指定验证模式则使用指定的。
2.如果没有指定则调用 detectValidationMode 自动检查
读取xml文件中的是否保护“DOCTYPE”,如果包含则是DTD,否则则是XSD

getEntityResolver() 方法

EntityResovle作用:SAX解析xml的时候首先读取xml文档上的声明,根据声明找相应的DTD定义。默认寻找规则:首先通过网络下载相应的DTD,并认证。网络下载是一个不确定的过程(网速问题、网络中断等),就会出现DTD找不到的情况。而EntityResovle提供了一个寻找DTD的自定义方法,一般我们回吧DTD放到项目中某文件夹下,直接读取本地的DTD交给SAX解析即可。避免了网络交换过程。

loadDocument() 方法


通过SAX解析xml。构造DocumentBuilderFactory解析xml。

registerBeanDefinitions() 方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
    }

protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
  1. 使用DefaultBeanDefinitionDocumentReader.class 构造BeanDefinitionDocumentReader 。
  2. 记录已经加载的Bean的个数
  3. 加载及注册Bean
  4. 返回这次加载的Bean的个数

从当前代码中可以看出注册加载Bean委托给 BeanDefinitionDocumentReader .registerBeanDefinitions() 方法处理

registerBeanDefinitions() 方法

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    Element root = doc.getDocumentElement();
    doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    if (this.delegate.isDefaultNamespace(root)) {
        //判断xml的beans标签属性中是否有profile属性,并验证跟web.xml中配置的信息是否匹配
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
    }
    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
    this.delegate = parent;
}
profile 用法
<!-- spring的applicationContext.xml中配置 -->
<beans profile="development">
    ......
</beans>
<beans profile="produce">
    ......
</beans>

<!-- web项目的web.xml中配置 -->
<context-param>
    <param-name>spring.profiles.default</param-name>
    <param-value>production</param-value>
</context-param>

可以使用profile来进行切换线上配置和开发环境配置,方便开发使用

parseBeanDefinitions() 方法

判断是自定义便签还是系统默认标签。
1.系统默认的标签调用parseDefaultElement方法解析
2.用户自定义标签使用parseCustomElement方法解析

parseDefaultElement() 方法

  1. 解析 import 标签
  2. 解析 alias 标签
  3. 解析 bean 标签
  4. 解析 beans 标签

parseCustomElement() 方法

主要解析自定义的标签内容
比如:

<!-- 用户自定义标签 -->
<bean id="xxx" class="test.XXX">
    <mybean:user username="zhangsan"/>
</bean>

<!-- 系统默认实现的自定义标签 -->
<tx:annotation-driven />

本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8    
点击这里快速进入简书

GIT地址:http://git.oschina.net/brucekankan/
点击这里快速进入GIT

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring 加载、解析applicationContext.xml 流程 的相关文章

随机推荐

  • NVIDIA驱动安装

    需要去英伟达官网下载适合自己电脑的版本 nvidia网页可以自己测出你的电脑所需要的型号 首先Ctrl Alt F1进入字符界面 删除原有驱动版本 sudo apt get purge nvidia sudo apt get autorem
  • 执行.sh文件(shell脚本)的几种方式

    第一种 要进到shell脚本所在文件夹中 sh helloworld sh 第二种 要进到shell脚本所在文件夹中 bash helloworld sh 第三种 要进到shell脚本所在文件夹中 helloworld sh 第四种 hom
  • uniapp app以及小程序图片添加水印

    const ctx uni createCanvasContext myCanvas const ctx1 uni createCanvasContext myCanvas1 uni getImageInfo src this carInf
  • UiBot如何使用CSS Selector

    UiBot默认的数据抓取可以抓取整个表格 但是有时候我们并不想抓取整个表格 比方说 我们想将下图所有的头像复制到Excel里 这个时候我们无法使用数据抓取功能 因为我们并不是想抓取数据 而是要操作网页里的元素 将上图头像复制到Excel里的
  • Centos7 扩展系统磁盘大小

    Centos7扩展系统磁盘大小 系统盘大小不足 需要扩展系统盘大小 需要添加一块硬盘作为要使用的系统盘的扩展 我的是原来sda就有空间没有分配 所以不用单独再加磁盘了 直接使用sda的未分配的空间 如果是单独新增的一个磁盘例如 dev sd
  • Jenkins 联动 飞书 以签名校验方式 推送测试报告通知消息

    1 获取 飞书 Bot webhook 和 secret 2 python脚本 参考 Song Estelle 的文章 这里重写了部分代码 以签名校验方式发送通知 记得安装相关依赖 usr bin python3 encoding utf
  • Java 中的阻塞队列

    Java 中的阻塞队列 1 ArrayBlockingQueue 由数组结构组成的有界阻塞队列 2 LinkedBlockingQueue 由链表结构组成的有界阻塞队列 3 PriorityBlockingQueue 支持优先级排序的无界阻
  • Python 日期字符串和时间戳解析方法详解

    原文链接 https dreamhomes top posts 202103091919 html 由于从事智能运维AIOps相关的算法研究 因此日常接触的最多就是时间序列相关的数据 在不同场景下时间字符串表示的格式可能都不相同 因此本文记
  • 解决git pull 报错insufficient permission for adding an object to repository database .git/objects

    这个报错是没有 git objects文件的写入权限 可能是 git objects被root角色创建 等到别的角色去操作时就产生了权限问题 所以解决这个问题就要改 git objects的权限 chown R username group
  • Java学习之:异常及其处理方式

    文章目录 1 所有异常都继承 Throwable 类 2 分类 3 为什么要处理异常 4 异常处理格式 5 完整异常信息的取得 5 1 常见的异常 6 throws throw 关键字 6 1 throws 用在定义方法上 6 1 1 用在
  • R中重命名数据框列名小技巧

    R中重命名数据框列名 文章目录 前言 一 基础包names函数和索引 二 使用dplyr rename函数 前言 R语言中两种修改数据框列名的小方法 创建名为df的数据框 一 基础包names函数和索引 将第二列名score修改为popul
  • 深入理解Android之Gradle

    转自 http blog csdn net innost article details 48228651 深入理解Android之Gradle 格式更加精美的PDF版请到 https pan baidu com s 1boG2cLD下载
  • SQL语句中的循环

    SQL语句中的循环 SQL语句中的循环类似于foreach循环 可以循环遍历某个表并进行新增 修改和删除的操作 SQL语句中的循环 使用SQL的游标来实现 上示例 declare ID int 声明变量 名称 类型 begin 开始 pri
  • setFocus不能生效的问题

    focusInEvent只有在对象显示出来的情况下设定setFocus才可以触发 这一点help手册里有说明 转一篇文章如下 http blog csdn net alex201030273437 article details 81937
  • CSV简单了解

    1 CSV介绍 CSV全称是Comma Separate Values 这种文件格式可以作为不同程序之间的数据交互的格式 csv就是一种纯文本文件 如 txt doc等 即是一组字符序列 字符之间已英文字符的逗号或制表符 Tab 分隔 语法
  • Python数据结构-----leetcode232.用栈实现队列

    目录 前言 方法讲解 示例 代码实现 232 用栈实现队列 前言 我们都知道队列的特征是先进先出 就跟排队一样先到先得 而栈的特征是后进后出 那这里我们怎么去通过两个栈来实现一个队列的功能呢 这一期我们一起来学习吧 方法讲解 这里需要准备好
  • 订单业务中的重要问题:超卖问题的解决方案

    订单业务中的重要问题 超卖问题的解决方案 我在做过的一些项目中都涉及到了订单的业务 如果你的项目中有关于订单的业务模块 那肯定说明你的项目中有卖商品的功能 所以有买卖场景就面临一个很常见的一个问题 那就是超卖问题 下面我就整理一下我在做项目
  • MyBatis与JDBC连接数据库所使用的url之间的差异

    1 在JDBC连接里是这样的 连接无误 2 在Mybatis里配置要这样 3 主要区别 说明 JDBC 方式连接 MySQL 不需要对 进行转义 而在Mybatis里要求一定要对 转义 4 如果是在properties文件里 不用转义的 在
  • IP静态路由实验报告

    一 将192 168 1 0 24划分为4个网段 192 168 1 0 26 192 168 1 64 26 192 168 1 128 26 192 168 1 192 26 1 取192 168 1 0 26继续划分 为主干道添加IP
  • Spring 加载、解析applicationContext.xml 流程

    概要 Spring 框架使用了BeanFactory 进行加载 xml 和生成 bean 实例 下面我们分析下Spring加载xml文件的过程 spring 版本是最新的 4 3 9 release 版本 示例 XmlBeanFactory