Spring源码阅读 之加载XML配置文件
2016-06-08 10:20
716 查看
以一个简单的例子开头:
场景类 Client.java
Bean实体类 Hello.java
配置文件bean.xml
接下来我们研究一下XML配置的加载过程。
1. ClassPathResource用于指向一个类路径下的一个资源,这里的com/dusk/bean/bean.xml表示的是包含Spring bean配置的XML文件。
通过上面的加粗代码可以知道配置文件是由XmlBeanDefinitionReader加载的。先说明一下,Spring的XML配置文件是由SAX处理的。关于SAX可以参考博文
SAX详解。
画了一个简单的序列图,有序列图的使用问题欢迎指正。
我们可以从图中看出来解析XML主要是由org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader类完成,这是Spring提供的默认解析类,当然你也可以定制自己的解析。
可以看出来Spring提供了一个很好的增强机制让你增强这个类,有两个空的方法预留给开发者。
这两个方法都是protected的,为了方便子类重写,并且都是空方法。
主要的解析XML方法是parseBeanDefinitions,如下:
可以看出来它是循环处理的,接着看详细内容:
以及
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//像BeanFactory中注册bean定义
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
<strong>//向IoC容器发送事件,表示解析和注册完成
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));</strong>
}
}以上就是Spring默认的解析XML的步骤。
场景类 Client.java
package com.dusk.bean; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Client { @Test public void test(){ Resource resource=new ClassPathResource("com/dusk/bean/bean.xml"); BeanFactory beanFactory=new XmlBeanFactory(resource); Hello hello=beanFactory.getBean(Hello.class); hello.sayHello(); } }
Bean实体类 Hello.java
package com.dusk.bean; public class Hello { private String name; public void setName(String name) { this.name = name; } public void sayHello(){ System.out.println("hello, "+name+"!"); } }
配置文件bean.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="hello" class="com.dusk.bean.Hello"> <property name="name" value="dusk"/> </bean> </beans>当然了输出结果是:
hello, dusk!
接下来我们研究一下XML配置的加载过程。
1. ClassPathResource用于指向一个类路径下的一个资源,这里的com/dusk/bean/bean.xml表示的是包含Spring bean配置的XML文件。
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * Create a new XmlBeanFactory with the given resource, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); <strong>this.reader.loadBeanDefinitions(resource);</strong> } }
通过上面的加粗代码可以知道配置文件是由XmlBeanDefinitionReader加载的。先说明一下,Spring的XML配置文件是由SAX处理的。关于SAX可以参考博文
SAX详解。
画了一个简单的序列图,有序列图的使用问题欢迎指正。
我们可以从图中看出来解析XML主要是由org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader类完成,这是Spring提供的默认解析类,当然你也可以定制自己的解析。
可以看出来Spring提供了一个很好的增强机制让你增强这个类,有两个空的方法预留给开发者。
/** * 解析自定义元素的地方 * <p>Allow the XML to be extensible by processing any custom element types first, * before we start to process the bean definitions. This method is a natural * extension point for any other custom pre-processing of the XML. * <p>The default implementation is empty. Subclasses can override this method to * convert custom elements into standard Spring bean definitions, for example. * Implementors have access to the parser's bean definition reader and the * underlying XML resource, through the corresponding accessors. * @see #getReaderContext() */ protected void preProcessXml(Element root) { }和
/** * Allow the XML to be extensible by processing any custom element types last, * after we finished processing the bean definitions. This method is a natural * extension point for any other custom post-processing of the XML. * <p>The default implementation is empty. Subclasses can override this method to * convert custom elements into standard Spring bean definitions, for example. * Implementors have access to the parser's bean definition reader and the * underlying XML resource, through the corresponding accessors. * @see #getReaderContext() */ protected void postProcessXml(Element root) { }
这两个方法都是protected的,为了方便子类重写,并且都是空方法。
主要的解析XML方法是parseBeanDefinitions,如下:
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
可以看出来它是循环处理的,接着看详细内容:
/* * 解析过程,对缺省的元素进行分析比如,bean */ private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { //import元素 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { //alias元素 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { //bean元素 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { //beans元素 // recurse(递归) doRegisterBeanDefinitions(ele); } }
以及
/** * Parse an "import" element and load the bean definitions * from the given resource into the bean factory. */ protected void importBeanDefinitionResource(Element ele) { String location = ele.getAttribute(RESOURCE_ATTRIBUTE); if (!StringUtils.hasText(location)) { getReaderContext().error("Resource location must not be empty", ele); return; } // Resolve system properties: e.g. "${user.dir}" location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set<Resource> actualResources = new LinkedHashSet<Resource>(4); // Discover whether the location is an absolute or relative URI boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix "classpath*:" } // Absolute or relative? if (absoluteLocation) { try { int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( "Failed to import bean definitions from URL location [" + location + "]", ele, ex); } } else { // No URL -> considering resource location as relative to the current file. try { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); if (relativeResource.exists()) { importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isDebugEnabled()) { logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); } } catch (IOException ex) { getReaderContext().error("Failed to resolve current resource location", ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); } /** * Process the given alias element, registering the alias with the registry. */ protected void processAliasRegistration(Element ele) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } }
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//像BeanFactory中注册bean定义
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
<strong>//向IoC容器发送事件,表示解析和注册完成
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));</strong>
}
}以上就是Spring默认的解析XML的步骤。
想要人前显贵必先人后受罪!
相关文章推荐
- JavaSE大纲图集
- java中的冒泡排序
- Java线程中yield与join方法的区别
- 0012_对象的多态性
- Crypto functions analysis in SCEP client java version
- 浅析Struts1和Struts2的Action线程安全问题
- 0011_接口
- The processing instruction target matching "[xX][mM][lL]" is not allowed.
- Java-集合、Map
- eclipse maven 导出项目依赖的jar包
- Java进阶学习第二十二天——上传与下载
- Spring mvc基于注解方式实现简单HelloWorld
- 深入分析JavaWeb 47 -- Struts2拦截器与文件上传下载
- 0010_抽象类
- 深入分析JavaWeb 46 -- Struts2数据校验与国际化
- 根据Java对象获取里面的方法,属性
- 图解用tree命令研究Windows目录、.net目录、项目目录、JDK目录
- Java编程体验:线程的7种状态及相互转换(图)
- Java-线程
- Spring学习之路