在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用delegate.parseCustomElement(ele)方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的demo。
一、自定义标签示例
- 定义POJO
下面定义一个Person类
package com.demo.beans.custom;
/**
*@author zhzhd
*@date 2018/6/11
*@package
*@describe
**/
public class Person {
private String userName;
private String sex;
private Integer age;
public Person(String name){
this.userName = name;
}
......省略setter和getter
}
- 定义一个文件描述组件
在webapp下创建person.xsd文件,内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.zhzhd.com/schema/person"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://www.zhzhd.com/schema/person">
<xsd:complexType name="person">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The unique identifier for a bean. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="userName" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The userName for a bean. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="sex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The sex of the bean. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="age" type="xsd:integer">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The age of the bean. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="person" type="person">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:schema>
- 自定义NamespaceHandler
自定义MyNamespaceHandler,继承NamespaceHandlerSupport,并且重写init()方法,实现如下:
package com.demo.beans.custom;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
*@author zhzhd
*@date 2018/6/11
*@package com.demo.beans.custom
*@describe
**/
public class MyNamespaceHandler extends NamespaceHandlerSupport{
public void init() {
registerBeanDefinitionParser("", new PersonBeanDefinitionParser());
}
}
- 实现自定义PersonBeanDefinitionParser
自定义实现PersonBeanDefinitionParser,并且重写Class getBeanClass(Element element)和doParse(Element element, BeanDefinitionBuilder bean)方法。getBeanClass方法返回当前bean的class,doParse解析自定义元素属性,实现如下:
package com.demo.beans.custom;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
*@author zhzhd
*@date 2018/6/11
*@package com.demo.beans.custom
*@describe
**/
public class PersonBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{
@Override
protected Class getBeanClass(Element element){
return Person.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder bean){
String name = element.getAttribute("name");
bean.addConstructorArgValue(name);
if (StringUtils.hasText(name)){
bean.addConstructorArgValue(name);
}
String age = element.getAttribute("age");
String sex = element.getAttribute("sex");
if (StringUtils.hasText(age)){
bean.addPropertyValue("age", Integer.parseInt(age));
}
if (StringUtils.hasText(age)){
bean.addPropertyValue("sex", sex);
}
}
}
- 创建spring.handlers和spring.schemas
http\://www.zhzhd.com/schema/person=com.demo.beans.custom.MyNamespaceHandler
http\://www.zhzhd.com/schema/person.xsd=person.xsd
- 在XML中配置bean以及测试
接下来,需要在spring的配置文件中加入命名空间信息,并且配置自定义bean,实现如下:
<?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:custom="http://www.zhzhd.com/schema/person"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.zhzhd.com/schema/person
http://www.zhzhd.com/schema/person.xsd">
<custom:person userName="zhzhd" sex="男" age="18" id="testPerson"></custom:person>
</beans>
测试demo如下:
public static void main(String[] args) {
BeanFactory beanFactory1 = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
ApplicationContext beanFactory = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"},true);
Person person = beanFactory.getBean("testPerson", Person.class);
System.out.println(JSON.toJSONString(person));
}
从上面的示例可以看到,我们定义了自定义节点的handler,spring在解析xml中节点或属性的时候,当遇到自定义节点和属性时,会调用响应的handler进行解析,下面具体分析自定义节点和属性解析的源码。
二、spring解析自定义标签的源码分析
首先,从xml元素的解析开始分析,在parseBeanDefinitions()方法中,判断如果是xml中节点或属性是自定义的,则调用BeanDefinitionParserDelegate的parseCustomElement()方法处理,下面是parseCustomElement()的实现:
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 读取命名空间url
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 解析命名空间,返回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;
}
// 解析自定义标签
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
从Element中获取到自定义命名空间uri后,交给DefaultNamespaceHandlerResolver的resolve()方法解析并实例化NamespaceHandler实例,具体实现如下:
public NamespaceHandler resolve(String namespaceUri) {
// 获取命名空间uri和handler类名关系map
Map<String, Object> handlerMappings = getHandlerMappings();
// 获取类名
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
// 实例化NamespaceHandler
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 初始化NamespaceHandler
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。