Spring系列-XML schema擴充套件機制

牛覓發表於2019-04-13

原文地址:www.jianshu.com/p/8639e5e9f…

從Spring 2.0版本開始,Spring提供了XML Schema可擴充套件機制,用於定義和配置Bean。完成XML自定義擴充套件,需要下面幾個步驟:

  1. 建立一個 XML Schema 檔案,描述自定義的合法構建模組,也就是xsd檔案。
  2. 自定義個處理器類,並實現NamespaceHandler介面。
  3. 自定義一個或多個解析器,實現BeanDefinitionParser介面(最關鍵的部分)。
  4. 註冊上面的元件到Spring IOC容器中。

按照上面的步驟,實現如下可擴充套件XML元素:

<myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>
複製程式碼

等價於

<bean id="dateFormat" class="java.text.SimpleDateFormat">
    <constructor-arg value="yyyy-HH-dd HH:mm"/>
    <property name="lenient" value="true"/>
</bean>
複製程式碼

1.自定義 XML Schema 檔案

<!-- myns.xsd (inside package org/springframework/samples/xml) -->
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.mycompany.com/schema/myns"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="http://www.mycompany.com/schema/myns"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified">

    <xsd:import namespace="http://www.springframework.org/schema/beans"/>

    <xsd:element name="dateformat">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="lenient" type="xsd:boolean"/>
                    <xsd:attribute name="pattern" type="xsd:string" use="required"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>
複製程式碼

2.自定義NamespaceHandler

NamespacespaceHandler用於解析配置檔案時遇到的特定名稱空間的所有元素。在我們的例子中,NamespaceHandler應該處理myns:dateformat元素的解析。

NamespaceHandler提供如下三個方法:

  • init(): NamespaceHandler被使用之前呼叫,完成NamespaceHandler的初始化。
  • BeanDefinition parse(Element, ParserContext): 當遇到頂層元素時被呼叫。
  • BeanDefinition decorate(Node,BeanDefinitionHandler,ParserContext): 當遇到一個屬性或者巢狀元素的時候呼叫。

Spring提供了一個預設的實現類NamespaceHandlerSupport,我們只需要在init的時候註冊每個元素的解析器即可。

public class DateformatNamespaceHandler extends NamespaceHandlerSupport { 

    public void init() { 
        registerBeanDefinitionParser("dateformat", new DeteformatDefinitionParser()); 
    }
}
複製程式碼

這裡實際用到了代理委託的概念,NamespaceHandlerSupport可以註冊任意個BeanDefinitionParserNamespaceHandlerSupport負責所有自定義元素的編排,而解析XML的工作委託給各個BeanDefinitionParser負責。

3.自定義BeanDefinitionParser

如果NamespapceHandler遇到元素型別(如:dateformat)已經有對應註冊的parser,則DateformatDefinitionParser會被呼叫,解析相應的屬性設定到Bean中。BeanDefinitionParser負責解析一個頂級元素。

Spring提供了AbstractSingleBeanDefinitionParser來處理繁重的解析工作,只需要實現兩個方法:

  • Class<?> getBeanClass(Element):返回元素的Class型別。
  • void doParse(Element element,BeanDefinitionBuilder builder):新增元素的屬性或者構造引數等等。
ppackage org.springframework.samples.xml;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import java.text.SimpleDateFormat;

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected Class getBeanClass(Element element) {
        return SimpleDateFormat.class;
    }

    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        // this will never be null since the schema explicitly requires that a value be supplied
        String pattern = element.getAttribute("pattern");
        bean.addConstructorArg(pattern);

        // this however is an optional property
        String lenient = element.getAttribute("lenient");
        if (StringUtils.hasText(lenient)) {
            bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
        }
    }

}
複製程式碼

4.註冊handler和schema

為了讓Spring在解析xml的時候能夠感知到我們的自定義元素,我們需要把NamespaceHandlerxsd檔案放到2個指定的配置檔案中,這2個檔案都位於META-INF目錄中

  • 'META-INF/spring.handlers':包含XML Schema URI到名稱空間處理程式類的對映。因此,對於我們的示例,我們需要編寫以下內容:

    http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.DateformatNamespaceHandler
    複製程式碼
  • META-INF/spring.schemas:包含XML Schema xsd到類路徑資源的對映。

    http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
    複製程式碼

5.在Spring XML配置中使用自定義擴充套件

<?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:myns="http://www.mycompany.com/schema/myns"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd">

    <!-- as a top-level bean -->
    <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>

    <bean id="jobDetailTemplate" abstract="true">
        <property name="dateFormat">
            <!-- as an inner bean -->
            <myns:dateformat pattern="HH:mm MM-dd-yyyy"/>
        </property>
    </bean>

</beans>
複製程式碼

相關文章