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委托给XmlBeanDefintionReader进行处理
1. 使用EncodeResource封装资源文件。如果指定编码则使用指定编码进行读取资源文件。
2. 判断该资源是否已经加载过
3. 构造InputStream实例,然后调用 doLoadBeanDefinitions() 方法
public class InputSource {
private String publicId;
private String systemId;
private InputStream byteStream;
private String encoding;
private Reader characterStream;
....
}
使用SAX解析、验证xml的时候需要使用到 publicId和systemId
1. 使用SAX解析xml获取Document对象
2. 根据返回的Document 注册 Bean 信息
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
判断xml的文档验证机制是DTD还是XSD
1.如果指定验证模式则使用指定的。
2.如果没有指定则调用 detectValidationMode 自动检查
读取xml文件中的是否保护“DOCTYPE”,如果包含则是DTD,否则则是XSD
EntityResovle作用:SAX解析xml的时候首先读取xml文档上的声明,根据声明找相应的DTD定义。默认寻找规则:首先通过网络下载相应的DTD,并认证。网络下载是一个不确定的过程(网速问题、网络中断等),就会出现DTD找不到的情况。而EntityResovle提供了一个寻找DTD的自定义方法,一般我们回吧DTD放到项目中某文件夹下,直接读取本地的DTD交给SAX解析即可。避免了网络交换过程。
通过SAX解析xml。构造DocumentBuilderFactory解析xml。
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));
}
从当前代码中可以看出注册加载Bean委托给 BeanDefinitionDocumentReader .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;
}
<beans profile="development">
......
beans>
<beans profile="produce">
......
beans>
<context-param>
<param-name>spring.profiles.defaultparam-name>
<param-value>productionparam-value>
context-param>
可以使用profile来进行切换线上配置和开发环境配置,方便开发使用
判断是自定义便签还是系统默认标签。
1.系统默认的标签调用parseDefaultElement方法解析
2.用户自定义标签使用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