Spring 加载、解析applicationContext.xml 流程

概要

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 用法


<beans profile="development">......
beans>
<beans profile="produce">......
beans>
<context-param><param-name>spring.profiles.defaultparam-name><param-value>productionparam-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


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部