天天看點

【Spring】Spring Framework Reference Documentation中文版37

42. Extensible XML authoring

擴充的XML編寫

42.1 Introduction

介紹

Since version 2.0, Spring has featured a mechanism for schema-based extensions to the basic Spring XML format for defining and configuring beans. This section is devoted to detailing how you would go about writing your own custom XML bean definition parsers and integrating such parsers into the Spring IoC container.

自從版本2.0開始,spring使用了基于schema擴充的政策對于基本的spring的xml格式化用于定義和配置bean。這一節将讨論你在自定義xml的bean的定義解析和內建的一些細節例如解析到spring的ioc容器中。

To facilitate the authoring of configuration files using a schema-aware XML editor, Spring’s extensible XML configuration mechanism is based on XML Schema. If you are not familiar with Spring’s current XML configuration extensions that come with the standard Spring distribution, please first read the appendix entitled???.

對于配置授權檔案使用schema的xml編輯器,spring擴充的xml配置政策是基于xml的schema的。如果你不熟悉spring目前的xml配置擴充對于spring現有的發行版本,請先閱讀整個附錄??

Creating new XML configuration extensions can be done by following these (relatively) simple steps:

建立一個新的xml配置擴充可以通過下面的簡單的步驟:

    Authoring an XML schema to describe your custom element(s).

編寫xml的schema來描述你自定義的元素。

    Coding a custom NamespaceHandler implementation (this is an easy step, don’t worry).

編碼一個自定義的NamespaceHandler實作(這是一個簡單的步驟,不用擔心)

    Coding one or more BeanDefinitionParser implementations (this is where the real work is done).

編碼一個或多個BeanDefinitionParser實作(就是你實際實作邏輯的位置)

    Registering the above artifacts with Spring (this too is an easy step).

注冊上面的artifacts于spring(這也是一個簡單的步驟)

What follows is a description of each of these steps. For the example, we will create an XML extension (a custom XML element) that allows us to configure objects of the type SimpleDateFormat (from the java.text package) in an easy manner. When we are done, we will be able to define bean definitions of type SimpleDateFormat like this:

下面是每個步驟的描述。例如,我們将建立一個xml的擴充(一個自定義的xml元素)允許我們來配置SimpleDateFormat類型的object(來自java.text包中)是一個簡單的方式。當我們完成之後,我們将可以定義SimpleDateFormat類型的bean如下:

<myns:dateformat id="dateFormat"

    pattern="yyyy-MM-dd HH:mm"

    lenient="true"/>

(Don’t worry about the fact that this example is very simple; much more detailed examples follow afterwards. The intent in this first simple example is to walk you through the basic steps involved.)

(不要擔心這個例子過于簡單,後面還有很多的案例。第一個簡單的案例的目的是完成基本步驟的調用。)

42.2 Authoring the schema

編寫schema

Creating an XML configuration extension for use with Spring’s IoC container starts with authoring an XML Schema to describe the extension. What follows is the schema we’ll use to configure SimpleDateFormat objects.

建立一個xml配置擴充用于使用spring的IOC容器開始編寫一個xml的schema來描述擴充。下面的schema我們将使用來配置SimpleDateFormat的object。

<!-- 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>

(The emphasized line contains an extension base for all tags that will be identifiable (meaning they have an id attribute that will be used as the bean identifier in the container). We are able to use this attribute because we imported the Spring-provided 'beans' namespace.)

(強調的行中包含了一個擴充用于所有的标簽将可以被辨認(意味着他們有id屬性将被使用作為bean的辨別符在容器中)。我們可以使用這個屬性因為我們引入了spring提供的beans的命名空間。)

The above schema will be used to configure SimpleDateFormat objects, directly in an XML application context file using the <myns:dateformat/> element.

上面的schema将被使用來配置SimpleDateFormat的object,直接在xml的應用上下文檔案中使用<myns:dateformat/>元素。

<myns:dateformat id="dateFormat"

    pattern="yyyy-MM-dd HH:mm"

    lenient="true"/>

Note that after we’ve created the infrastructure classes, the above snippet of XML will essentially be exactly the same as the following XML snippet. In other words, we’re just creating a bean in the container, identified by the name 'dateFormat' of type SimpleDateFormat, with a couple of properties set.

注意在我們建立基礎類之後,上面的xml片段将和下面的xml片段是相似的。換句話說,我們隻是建立了一個在容器中的bean,通過SimpleDateFormat類型的dateFormat名字來定義,使用一些屬性的集合。

<bean id="dateFormat" class="java.text.SimpleDateFormat">

    <constructor-arg value="yyyy-HH-dd HH:mm"/>

    <property name="lenient" value="true"/>

</bean>

[Note]

注意

The schema-based approach to creating configuration format allows for tight integration with an IDE that has a schema-aware XML editor. Using a properly authored schema, you can use autocompletion to have a user choose between several configuration options defined in the enumeration.

基于schema的方式來建立一個配置格式允許簡化配置使用IDE因為schema的xml編輯器。使用一個适當的schema,你可以使用自動完成對于使用者選擇一些配置定義在配置中。

42.3 Coding a NamespaceHandler

編寫一個NamespaceHandler

In addition to the schema, we need a NamespaceHandler that will parse all elements of this specific namespace Spring encounters while parsing configuration files. The NamespaceHandler should in our case take care of the parsing of the myns:dateformat element.

此外對于schema,我們需要一個NamespaceHandler來解析所有spring在配置檔案中發現的指定命名空間中的元素。NamespaceHandler在我們的例子中解析myns:dateformat元素。

The NamespaceHandler interface is pretty simple in that it features just three methods:

NamespaceHandler接口是十分簡單的并且包含三個方法:

    init() - allows for initialization of the NamespaceHandler and will be called by Spring before the handler is used

init————允許初始化NamespaceHandler并且将被spring調用在處理器被使用之前。

    BeanDefinition parse(Element, ParserContext) - called when Spring encounters a top-level element (not nested inside a bean definition or a different namespace). This method can register bean definitions itself and/or return a bean definition.

BeanDefinition parse(Element, ParserContext)————當spring解析到底層元素時被調用(沒有内置在bean的定義中或另一個命名空間)。這個方法可以注冊bean的定義本身或傳回一個bean的定義。

    BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext) - called when Spring encounters an attribute or nested element of a different namespace. The decoration of one or more bean definitions is used for example with theout-of-the-box scopes Spring 2.0 supports. We’ll start by highlighting a simple example, without using decoration, after which we will show decoration in a somewhat more advanced example.

BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext)————spring解析一個屬性或不同命名空間的内置元素時被調用。一個或多個bean定義的描述被使用在spring2.0支援中。我們将使用一個簡單的例子,不需要使用描述,在我們展示一個更加進階的例子之後。

Although it is perfectly possible to code your own NamespaceHandler for the entire namespace (and hence provide code that parses each and every element in the namespace), it is often the case that each top-level XML element in a Spring XML configuration file results in a single bean definition (as in our case, where a single <myns:dateformat/> element results in a single SimpleDateFormat bean definition). Spring features a number of convenience classes that support this scenario. In this example, we’ll make use the NamespaceHandlerSupport class:

盡管對于你的代碼有你自己的NamespaceHandler用于整個命名空間(并且提供代碼來解析每一個命名空間中的元素),對于每個spring的xml配置檔案中的頂層元素在一個bean的定義中(在我們的例子中,一個簡單的<myns:dateformat/>元素傳回一個簡單的SimpleDateFormat的bean的定義)。spring的特性對于一些友善的類來支援這些場景。在這個例子中,我們将會使用NamespaceHandlerSupport類。

package org.springframework.samples.xml;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class MyNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {

        registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());

    }

}

The observant reader will notice that there isn’t actually a whole lot of parsing logic in this class. Indeed…​ the NamespaceHandlerSupport class has a built in notion of delegation. It supports the registration of any number of BeanDefinitionParser instances, to which it will delegate to when it needs to parse an element in its namespace. This clean separation of concerns allows a NamespaceHandler to handle the orchestration of the parsing of all of the custom elements in its namespace, while delegating to BeanDefinitionParsers to do the grunt work of the XML parsing; this means that each BeanDefinitionParser will contain just the logic for parsing a single custom element, as we can see in the next step

善于觀察的讀者将意識到他并且不是這個的解析邏輯在這個類中。作為替代,NamespaceHandlerSupport類有内置的代表方式。他支援注冊一定數量的BeanDefinitionParser執行個體,将被委托來解析命名空間中的元素。這些分開的邏輯允許NamespaceHandler來處了解析每一個自定義元素在命名空間中的集合,委托給BeanDefinitionParsers來處理xml的解析工作;這意味着每個BeanDefinitionParser将包含解析單個自定義元素的邏輯,我們在下一步中就能看到。

42.4 BeanDefinitionParser

A BeanDefinitionParser will be used if the NamespaceHandler encounters an XML element of the type that has been mapped to the specific bean definition parser (which is 'dateformat' in this case). In other words, the BeanDefinitionParser is responsible for parsing one distinct top-level XML element defined in the schema. In the parser, we’ll have access to the XML element (and thus its subelements too) so that we can parse our custom XML content, as can be seen in the following example:

BeanDefinitionParser将被使用如果NamespaceHandler處理一個xml元素來比對指定的bean定義解析器(在這個例子中是dateformat類型)。換句話說,BeanDefinitionParser用于接卸每個單獨的定義xml元素在schema中。在解析器中,我們将通路xml元素(并且和這些子元素)是以我們可以解析我們自定義的xml元素,是以可以被看到下面的例子:

package 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 { 1

    protected Class getBeanClass(Element element) {

        return SimpleDateFormat.class; 2

    }

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

        }

    }

}

1 We use the Spring-provided AbstractSingleBeanDefinitionParser to handle a lot of the basic grunt work of creating a single BeanDefinition.

我們使用spring提供上司AbstractSingleBeanDefinitionParser來處理基本的邏輯有關建立單一的BeanDefinition。

2 We supply the AbstractSingleBeanDefinitionParser superclass with the type that our single BeanDefinition will represent.

我們應用AbstractSingleBeanDefinitionParser超類使用我們的單一的BeanDefinition的類型。

In this simple case, this is all that we need to do. The creation of our single BeanDefinition is handled by the AbstractSingleBeanDefinitionParser superclass, as is the extraction and setting of the bean definition’s unique identifier.

在這個簡單的例子中,我們需要這麼做。單一的BeanDefinition的建立被處理通過AbstractSingleBeanDefinitionParser超類,是以抽取和設定bean的定義是唯一的辨別符。

42.5 Registering the handler and the schema

注冊處理器和schema

The coding is finished! All that remains to be done is to somehow make the Spring XML parsing infrastructure aware of our custom element; we do this by registering our custom namespaceHandler and custom XSD file in two special purpose properties files. These properties files are both placed in a 'META-INF' directory in your application, and can, for example, be distributed alongside your binary classes in a JAR file. The Spring XML parsing infrastructure will automatically pick up your new extension by consuming these special properties files, the formats of which are detailed below.

代碼已經完成了。所有保留的部分已經做完了有關展示spring的xml的解析結構對于我們自定義的元素;我們通過注冊我們自定義的namespaceHandler和自定義的xsd檔案制定兩個特定屬性檔案。這些屬性檔案放置在你應用的'META-INF'目錄中,并且例如,可以獨立于你在JAR檔案中的class。spirng的xml解析架構将自動使用你的新的擴充通過處理這些特定的屬性檔案以及下面定義的格式。

42.5.1 'META-INF/spring.handlers'

The properties file called 'spring.handlers' contains a mapping of XML Schema URIs to namespace handler classes. So for our example, we need to write the following:

屬性名字為'spring.handlers'包含xml的schema的URI到命名空間處理器類的比對關系。是以在我們的例子中,我們需要書寫如下:

http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler

(The ':' character is a valid delimiter in the Java properties format, and so the ':' character in the URI needs to be escaped with a backslash.)

(冒号字元是一個合法的分隔符在Java的屬性格式中,是以冒号在URL需要被轉義編碼。)

The first part (the key) of the key-value pair is the URI associated with your custom namespace extension, and needs to match exactly the value of the 'targetNamespace' attribute as specified in your custom XSD schema.

第一部分(鍵)對于鍵值對是URI比對你自定義的命名空間擴充,并且需要比對'targetNamespace'屬性的值定義在你自定義的xsd的schema中。

42.5.2 'META-INF/spring.schemas'

The properties file called 'spring.schemas' contains a mapping of XML Schema locations (referred to along with the schema declaration in XML files that use the schema as part of the 'xsi:schemaLocation' attribute) to classpath resources. This file is needed to prevent Spring from absolutely having to use a default EntityResolver that requires Internet access to retrieve the schema file. If you specify the mapping in this properties file, Spring will search for the schema on the classpath (in this case 'myns.xsd' in the 'org.springframework.samples.xml' package):

屬性檔案名字為'spring.schemas'包含了比對對于xml的schema的位置(應用定義在xml檔案中的schema的定義使用schema作為'xsi:schemaLocation'屬性的一部分)以及classpath中的資源。這個檔案需要阻止spring使用絕對的預設的EntityResolver及要求網絡通路來接收schema檔案。如果你定義了比對屬性檔案,spring将需找schema在classpath中(在這個例子中是myns.xsd在'org.springframework.samples.xml'包中):

http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd

The upshot of this is that you are encouraged to deploy your XSD file(s) right alongside the NamespaceHandler and BeanDefinitionParser classes on the classpath.

這個結局是你需要正确的處理你的xsd檔案使用NamespaceHandler和BeanDefinitionParser類在你的classpath中。

42.6 Using a custom extension in your Spring XML configuration

使用自定義的擴充在你的spring的xml配置中

Using a custom extension that you yourself have implemented is no different from using one of the 'custom' extensions that Spring provides straight out of the box. Find below an example of using the custom <dateformat/> element developed in the previous steps in a Spring XML configuration file.

使用一個自定義的擴充對于你來說需要實作不管是使用自定義的spring提供的擴充還是spring直接提供的。下面的例子使用的自定義的<dateformat/>元素部署在之前步驟中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>

42.7 Meatier examples

Find below some much meatier examples of custom XML extensions.

下面是metier的例子有關自定義的xml擴充。

42.7.1 Nesting custom tags within custom tags

内置的自定義标簽使用自定義的标簽

This example illustrates how you might go about writing the various artifacts required to satisfy a target of the following configuration:

這個例子展示了你使用不同的artifacts對于下面的配置:

<?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:foo="http://www.foo.com/schema/component"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.foo.com/schema/component http://www.foo.com/schema/component/component.xsd">

    <foo:component id="bionic-family" name="Bionic-1">

        <foo:component name="Mother-1">

            <foo:component name="Karate-1"/>

            <foo:component name="Sport-1"/>

        </foo:component>

        <foo:component name="Rock-1"/>

    </foo:component>

</beans>

The above configuration actually nests custom extensions within each other. The class that is actually configured by the above <foo:component/> element is the Component class (shown directly below). Notice how the Component class does not expose a setter method for the 'components' property; this makes it hard (or rather impossible) to configure a bean definition for the Component class using setter injection.

上面的配置實際上内置了自定義的擴充。上面的<foo:component/>元素配置的類是Component類(直接展示如下)。注意Component類沒有暴露設定方法對于components屬性;這使得很難(幾乎不可能)來配置一個bean的定義對于COmponent的類使用設定的注入方式。

package com.foo;

import java.util.ArrayList;

import java.util.List;

public class Component {

    private String name;

    private List<Component> components = new ArrayList<Component> ();

    // mmm, there is no setter method for the 'components'

    public void addComponent(Component component) {

        this.components.add(component);

    }

    public List<Component> getComponents() {

        return components;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

}

The typical solution to this issue is to create a custom FactoryBean that exposes a setter property for the 'components' property.

典型的解決方案是建立一個自定義的FactoryBean來暴露一個設定屬性對于components屬性。

package com.foo;

import org.springframework.beans.factory.FactoryBean;

import java.util.List;

public class ComponentFactoryBean implements FactoryBean<Component> {

    private Component parent;

    private List<Component> children;

    public void setParent(Component parent) {

        this.parent = parent;

    }

    public void setChildren(List<Component> children) {

        this.children = children;

    }

    public Component getObject() throws Exception {

        if (this.children != null && this.children.size() > 0) {

            for (Component child : children) {

                this.parent.addComponent(child);

            }

        }

        return this.parent;

    }

    public Class<Component> getObjectType() {

        return Component.class;

    }

    public boolean isSingleton() {

        return true;

    }

}

This is all very well, and does work nicely, but exposes a lot of Spring plumbing to the end user. What we are going to do is write a custom extension that hides away all of this Spring plumbing. If we stick to the steps described previously, we’ll start off by creating the XSD schema to define the structure of our custom tag.

這是很好的可以工作的,但是暴露了很多spring的部分給終端使用者。我們書寫的自定義擴充應該隐藏所有的spring的内置内容。如果我們按照之前的步驟,我們需要開始建立xsd的schema來定義我們自定義标簽的結構。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://www.foo.com/schema/component"

        xmlns:xsd="http://www.w3.org/2001/XMLSchema"

        targetNamespace="http://www.foo.com/schema/component"

        elementFormDefault="qualified"

        attributeFormDefault="unqualified">

    <xsd:element name="component">

        <xsd:complexType>

            <xsd:choice minOccurs="0" maxOccurs="unbounded">

                <xsd:element ref="component"/>

            </xsd:choice>

            <xsd:attribute name="id" type="xsd:ID"/>

            <xsd:attribute name="name" use="required" type="xsd:string"/>

        </xsd:complexType>

    </xsd:element>

</xsd:schema>

We’ll then create a custom NamespaceHandler.

我們将建立一個自定義的NamespaceHandler。

package com.foo;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class ComponentNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {

        registerBeanDefinitionParser("component", new ComponentBeanDefinitionParser());

    }

}

Next up is the custom BeanDefinitionParser. Remember that what we are creating is a BeanDefinition describing a ComponentFactoryBean.

下一步對于自定義的BeanDefinitionParser。記住我們建立的是一個BeanDefinition描述了一個ComponentFactoryBean。

package com.foo;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.beans.factory.support.AbstractBeanDefinition;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

import org.springframework.beans.factory.support.ManagedList;

import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;

import org.springframework.beans.factory.xml.ParserContext;

import org.springframework.util.xml.DomUtils;

import org.w3c.dom.Element;

import java.util.List;

public class ComponentBeanDefinitionParser extends AbstractBeanDefinitionParser {

    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {

        return parseComponentElement(element);

    }

    private static AbstractBeanDefinition parseComponentElement(Element element) {

        BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean.class);

        factory.addPropertyValue("parent", parseComponent(element));

        List<Element> childElements = DomUtils.getChildElementsByTagName(element, "component");

        if (childElements != null && childElements.size() > 0) {

            parseChildComponents(childElements, factory);

        }

        return factory.getBeanDefinition();

    }

    private static BeanDefinition parseComponent(Element element) {

        BeanDefinitionBuilder component = BeanDefinitionBuilder.rootBeanDefinition(Component.class);

        component.addPropertyValue("name", element.getAttribute("name"));

        return component.getBeanDefinition();

    }

    private static void parseChildComponents(List<Element> childElements, BeanDefinitionBuilder factory) {

        ManagedList<BeanDefinition> children = new ManagedList<BeanDefinition>(childElements.size());

        for (Element element : childElements) {

            children.add(parseComponentElement(element));

        }

        factory.addPropertyValue("children", children);

    }

}

Lastly, the various artifacts need to be registered with the Spring XML infrastructure.

最後,不同artifacts需要被注冊使用spring的xml結構。

# in 'META-INF/spring.handlers'

http\://www.foo.com/schema/component=com.foo.ComponentNamespaceHandler

# in 'META-INF/spring.schemas'

http\://www.foo.com/schema/component/component.xsd=com/foo/component.xsd

42.7.2 Custom attributes on 'normal' elements

自定義屬性對于normal元素

Writing your own custom parser and the associated artifacts isn’t hard, but sometimes it is not the right thing to do. Consider the scenario where you need to add metadata to already existing bean definitions. In this case you certainly don’t want to have to go off and write your own entire custom extension; rather you just want to add an additional attribute to the existing bean definition element.

使用你自定義的解析器和相關的artifacts不是困難的,但是有時他不是正确的方式。考慮這樣的場景,你需要添加一個中繼資料對于已有的bean的定義。在這個例子中你不需要書寫你自己的整個自定義擴充;你隻是添加一個額外的屬性對于已有的bean定義元素。

By way of another example, let’s say that the service class that you are defining a bean definition for a service object that will (unknown to it) be accessing a clustered JCache, and you want to ensure that the named JCache instance is eagerly started within the surrounding cluster:

對于另一個例子,我們呢看到你定義的服務類定義了一個bean的定義用于服務object(對其不知)可以被一個叢集的JCache通路,并且你保證名字為JCache的執行個體是使用下面的叢集開始的:

<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"

        jcache:cache-name="checking.account">

    <!-- other dependencies here... -->

</bean>

What we are going to do here is create another BeanDefinition when the 'jcache:cache-name' attribute is parsed; this BeanDefinition will then initialize the named JCache for us. We will also modify the existing BeanDefinition for the 'checkingAccountService' so that it will have a dependency on this new JCache-initializing BeanDefinition.

我們需要做的是建立另一個BeanDefinition當'jcache:cache-name'屬性被解析的時候,這個BeanDefinition将初始化JCache。我們将修改已有的BeanDefinition用于'checkingAccountService'是以他将會依賴心的JCache的初始化BeanDefinition。

package com.foo;

public class JCacheInitializer {

    private String name;

    public JCacheInitializer(String name) {

        this.name = name;

    }

    public void initialize() {

        // lots of JCache API calls to initialize the named cache...

    }

}

Now onto the custom extension. Firstly, the authoring of the XSD schema describing the custom attribute (quite easy in this case).

現在對于自定義擴充。首先,XSD的授權描述了自定義屬性(在這個例子中是很簡單的)。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://www.foo.com/schema/jcache"

        xmlns:xsd="http://www.w3.org/2001/XMLSchema"

        targetNamespace="http://www.foo.com/schema/jcache"

        elementFormDefault="qualified">

    <xsd:attribute name="cache-name" type="xsd:string"/>

</xsd:schema>

Next, the associated NamespaceHandler.

後面是相關的NamespaceHandler。

package com.foo;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class JCacheNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {

        super.registerBeanDefinitionDecoratorForAttribute("cache-name",

            new JCacheInitializingBeanDefinitionDecorator());

    }

}

Next, the parser. Note that in this case, because we are going to be parsing an XML attribute, we write a BeanDefinitionDecorator rather than a BeanDefinitionParser.

後面是解析器。注意在這個例子中,因為我們希望解析一個xml屬性,我們需要提供一個BeanDefinitionDecorator而不是BeanDefinitionParser。

package com.foo;

import org.springframework.beans.factory.config.BeanDefinitionHolder;

import org.springframework.beans.factory.support.AbstractBeanDefinition;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

import org.springframework.beans.factory.xml.BeanDefinitionDecorator;

import org.springframework.beans.factory.xml.ParserContext;

import org.w3c.dom.Attr;

import org.w3c.dom.Node;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

public class JCacheInitializingBeanDefinitionDecorator implements BeanDefinitionDecorator {

    private static final String[] EMPTY_STRING_ARRAY = new String[0];

    public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder,

            ParserContext ctx) {

        String initializerBeanName = registerJCacheInitializer(source, ctx);

        createDependencyOnJCacheInitializer(holder, initializerBeanName);

        return holder;

    }

    private void createDependencyOnJCacheInitializer(BeanDefinitionHolder holder,

            String initializerBeanName) {

        AbstractBeanDefinition definition = ((AbstractBeanDefinition) holder.getBeanDefinition());

        String[] dependsOn = definition.getDependsOn();

        if (dependsOn == null) {

            dependsOn = new String[]{initializerBeanName};

        } else {

            List dependencies = new ArrayList(Arrays.asList(dependsOn));

            dependencies.add(initializerBeanName);

            dependsOn = (String[]) dependencies.toArray(EMPTY_STRING_ARRAY);

        }

        definition.setDependsOn(dependsOn);

    }

    private String registerJCacheInitializer(Node source, ParserContext ctx) {

        String cacheName = ((Attr) source).getValue();

        String beanName = cacheName + "-initializer";

        if (!ctx.getRegistry().containsBeanDefinition(beanName)) {

            BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer.class);

            initializer.addConstructorArg(cacheName);

            ctx.getRegistry().registerBeanDefinition(beanName, initializer.getBeanDefinition());

        }

        return beanName;

    }

}

Lastly, the various artifacts need to be registered with the Spring XML infrastructure.

最後,不同的artifacts需要被注冊使用spring的xml架構。

# in 'META-INF/spring.handlers'

http\://www.foo.com/schema/jcache=com.foo.JCacheNamespaceHandler

# in 'META-INF/spring.schemas'

http\://www.foo.com/schema/jcache/jcache.xsd=com/foo/jcache.xsd

42.8 Further Resources

更多的資源

Find below links to further resources concerning XML Schema and the extensible XML support described in this chapter.

下面的連結指向更多的資源有關xml的schema和擴充的xml支援描述在這些章節中。

    The XML Schema Part 1: Structures Second Edition

xml的schema部分一:結構第二版

    The XML Schema Part 2: Datatypes Second Edition

xml的schema部分二:資料類型第二版