在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); } } }