在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每个节点,判断是否为默认命名空间中的节点,若是是非默认命名空间的,调用delegate.parseCustomElement(ele)方法进行处理。在学习自定义标签解析以前,先写一个自定义标签的demo。web
下面定义一个Person类spring
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文件,内容以下:app
<?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>
自定义MyNamespaceHandler,继承NamespaceHandlerSupport,而且重写init()方法,实现以下:dom
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,而且重写Class getBeanClass(Element element)和doParse(Element element, BeanDefinitionBuilder bean)方法。getBeanClass方法返回当前bean的class,doParse解析自定义元素属性,实现以下:webapp
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); } } }
http\://www.zhzhd.com/schema/person=com.demo.beans.custom.MyNamespaceHandler
http\://www.zhzhd.com/schema/person.xsd=person.xsd
接下来,须要在spring的配置文件中加入命名空间信息,而且配置自定义bean,实现以下:ide
<?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进行解析,下面具体分析自定义节点和属性解析的源码。学习
首先,从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实例,具体实现以下:ui
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); } } }