Spring中,从AbstractXmlApplicationContext开始,经过对NamespaceHandler & BeanDefinitionParser,来实现自定义xml配置的功能。php
xml文件的加载,从AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory)中开始实现:java
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// 加载bean定义
loadBeanDefinitions(beanDefinitionReader);
}
复制代码
加载过程是由XmlBeanDefinitionReader实现的,XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader,过程以下:node
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)) {
// 若是是默认的namespace,则执行parseDefaultElement加载默认的xml元素,如import, alias, bean等;
parseDefaultElement(ele, delegate);
}
else {
// 不然执行BeanDefinitionParserDelegate.parseCustomElement(root)加载自定义的xml元素。
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
复制代码
BeanDefinitionParserDelegate.parseCustomElement中获取了自定义的namespace,并根据namespace获取NamespaceHandler,而后执行NamespaceHandler.parse,并返回BeanDefinition.spring
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
// 获取namespace
String namespaceUri = getNamespaceURI(ele);
// 获取对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 返回BeanDefinition
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
复制代码
具体过程以下:bash
1.this.readerContext.getNamespaceHandlerResolver() :ide
2.DefaultNamespaceHandlerResolver.resolve :post
public class ServerConfig {
private String host;
private int port;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
复制代码
建立xsd,并放到META-INF下;如文件名为custom.xsd。ui
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="custom" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="custom" >
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:element name="serverConfig">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="host" type="xsd:string" />
<xsd:attribute name="port" type="xsd:int" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
复制代码
继承AbstractSingleBeanDefinitionParser,并重写getBeanClass和doParse两个方法,解析custom.xsd中定义的xml节点的属性。this
public class ServerConfigBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return ServerConfig.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name = element.getAttribute("host");
String port = element.getAttribute("port");
if (StringUtils.hasText(name)) {
builder.addPropertyValue("host", name);
}
if (StringUtils.hasText(port)) {
builder.addPropertyValue("port", Integer.valueOf(port));
}
}
}
复制代码
继承NamespaceHandlerSupport,重写init()方法:将custom.xsd中定义的xml根节点,注册为上面实现的ServerConfigBeanDefinitionParser对象。spa
public class ServerConfigNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("serverConfig", new ServerConfigBeanDefinitionParser());
}
}
复制代码
classpath下,新建文件 META-INF/spring.schemas,并写入如下内容:
custom.xsd=classpath:META-INF/custom.xsd
复制代码
classpath下,新建文件 META-INF/spring.hander,并写入如下内容:
custom=xxx.xxx.ServerConfigNamespaceHandler
复制代码
在spring的xml配置中,引入上面声明的xsd:
<?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:tinyrpc="custom" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd custom META-INF/custom.xsd" default-lazy-init="false" default-autowire="byName">
<custom:serverConfig id=”testServer" host=”localhost" port=”8888"></custom:serverConfig>
</beans>
复制代码
便可声明一个id为testServer的Bean。
综上所述,实现基本的自定义xml,按照以下几个步骤便可:
Spring中的aop配置,事务配置等,阿里Dubbo,美团的Pigeon 中自定义的xml配置,均由此方式实现。
了解spring的加载过程,可参考Spring加载过程及核心类。