前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring 加载、解析applicationContext.xml 流程

Spring 加载、解析applicationContext.xml 流程

作者头像
java404
发布2018-05-18 12:28:49
1.9K0
发布2018-05-18 12:28:49
举报
文章被收录于专栏:java 成神之路java 成神之路

概要

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

示例

代码语言:javascript
复制
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 类结构
代码语言:javascript
复制
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()
代码语言:javascript
复制
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() 方法
代码语言:javascript
复制
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() 方法

代码语言:javascript
复制
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 用法
代码语言:javascript
复制
<!-- 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() 方法

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

代码语言:javascript
复制
<!-- 用户自定义标签 -->
<bean id="xxx" class="test.XXX">
    <mybean:user username="zhangsan"/>
</bean>

<!-- 系统默认实现的自定义标签 -->
<tx:annotation-driven />
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.09.17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概要
  • 示例
  • 源码解析
    • XmlBeanFactory.java
      • XmlBeanDefintionReader.java
        • doLoadBeanDefinitions() 方法
          • doLoadDocument()
            • getValidationModeForResource(resource)
              • getEntityResolver() 方法
                • loadDocument() 方法
                  • registerBeanDefinitions() 方法
                  • registerBeanDefinitions() 方法
                    • profile 用法
                      • parseBeanDefinitions() 方法
                      • parseDefaultElement() 方法
                      • parseCustomElement() 方法
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档