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加載過程及核心類。