Spring源碼閱讀——ClassPathXmlApplicationContext(四)

在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍歷每個節點,判斷是否爲默認命名空間中的節點,若是是非默認命名空間的,調用delegate.parseCustomElement(ele)方法進行處理。在學習自定義標籤解析以前,先寫一個自定義標籤的demo。web

1、自定義標籤示例

  • 定義POJO

下面定義一個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>
  • 自定義NamespaceHandler

自定義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

自定義實現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);
        }
    }
}
  • 建立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,實現以下: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進行解析,下面具體分析自定義節點和屬性解析的源碼。學習

2、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實例,具體實現以下: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);
            }
        }
    }
相關文章
相關標籤/搜索