您的位置:首页 > 编程语言 > Java开发

Spring源码阅读 之加载XML配置文件

2016-06-08 10:20 716 查看
以一个简单的例子开头:

场景类 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的步骤。

想要人前显贵必先人后受罪!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: