Spring中,从AbstractXmlApplicationContext开始,通过对NamespaceHandler & BeanDefinitionParser,来实现自定义xml配置的功能。
xml文件的加载,从AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory)中开始实现:
@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,过程如下:
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.
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)); } 复制代码
具体过程如下:
1.this.readerContext.getNamespaceHandlerResolver() :
2.DefaultNamespaceHandlerResolver.resolve :
所以,一般直接继承NamespaceHandlerSupport 或 AbstractSingleBeanDefinitionParser即可。
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。
<?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节点的属性。
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对象。
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加载过程及核心类 。