【翻译 Spring 5.0.4.RELEASE】9. Appendix

9. Appendix

9.1. XML Schemas

附录的这一部分列出了与核心容器相关的XML模式。

9.1.1. The util schema

顾名思义,util标签处理常见的工具配置问题,例如配置集合,引用常量等。 要在util模式中使用标签,您需要在Spring XML配置文件的顶部有以下前导码; 下面代码片段中的文本引用了正确的模式,以便您可以使用util命名空间中的标签。

<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- bean definitions here -->

</beans>

<util:constant/>

之前…

<bean id="..." class="...">
    <property name="isolation">
        <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
                class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
    </property>
</bean>

上面的配置使用Spring FactoryBean实现FieldRetrievingFactoryBean将bean上的isolation属性值设置为java.sql.Connection.TRANSACTION_SERIALIZABLE常量的值。 这一切都很好,但它有点冗长,并且(不必要地)将Spring的内部管道暴露给最终用户。

下面的基于XML模式的版本更加简洁,清楚地表达了开发者的意图(’注入这个常量值’),并且它更好。

<bean id="..." class="...">
    <property name="isolation">
        <util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
    </property>
</bean>

Setting a bean property or constructor arg from a field value

FieldRetrievingFactoryBean是一个检索静态或非静态字段值的FactoryBean。 它通常用于检索公共静态最终常量,然后可以用它来为另一个bean设置属性值或构造函数arg。

通过使用staticField属性查找下面的示例,其中显示了静态字段的显示方式:

<bean id="myField"
        class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</bean>

还有一个方便的用法形式,其中静态字段被指定为bean名称:

<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
        class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

这确实意味着bean id不再有任何选择(因此任何引用它的bean也将不得不使用这个更长的名称),但是这种形式非常简洁并且非常便于使用 由于不必为bean引用指定id,因此需要一个内部bean:

<bean id="..." class="...">
    <property name="isolation">
        <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
                class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
    </property>
</bean>

也可以访问另一个bean的非静态(实例)字段,如FieldRetrievingFactoryBean类的API文档中所述。

将枚举值注入bean作为属性或构造函数参数在Spring中非常容易实现,因为您实际上不必对Spring内部进行任何操作或了解任何内容(甚至不需要关于FieldRetrievingFactoryBean等类)。 让我们来看一个例子,看看如何轻松地注入一个枚举值; 考虑这个枚举:

package javax.persistence;

public enum PersistenceContextType {

    TRANSACTION,
    EXTENDED
}

现在考虑一个类型为PersistenceContextType的setter:

package example;

public class Client {

    private PersistenceContextType persistenceContextType;

    public void setPersistenceContextType(PersistenceContextType type) {
        this.persistenceContextType = type;
    }
}

和相应的bean定义:

<bean class="example.Client">
    <property name="persistenceContextType" value="TRANSACTION"/>
</bean>

<util:property-path/>

之前…

<!-- target bean to be referenced by name -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
    <property name="age" value="10"/>
    <property name="spouse">
        <bean class="org.springframework.beans.TestBean">
            <property name="age" value="11"/>
        </bean>
    </property>
</bean>

<!-- will result in 10, which is the value of property 'age' of bean 'testBean' -->
<bean id="testBean.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

上面的配置使用Spring FactoryBean实现PropertyPathFactoryBean创建一个名为testBean.age的bean(类型为int),其值等于testBean bean的age属性。

之后…

<!-- target bean to be referenced by name -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
    <property name="age" value="10"/>
    <property name="spouse">
        <bean class="org.springframework.beans.TestBean">
            <property name="age" value="11"/>
        </bean>
    </property>
</bean>

<!-- will result in 10, which is the value of property 'age' of bean 'testBean' -->
<util:property-path id="name" path="testBean.age"/>

<property-path />标签的path属性的值遵循beanName.beanProperty的形式。

Using <util:property-path/>to set a bean property or constructor-argument

PropertyPathFactoryBean是一个FactoryBean,用于评估给定目标对象上的属性路径。 目标对象可以直接指定或通过bean名称指定。 这个值然后可以在另一个bean定义中用作属性值或构造函数参数。

以下是一个按名称使用另一个bean的路径的示例:

// target bean to be referenced by name
<bean id="person" class="org.springframework.beans.TestBean" scope="prototype">
    <property name="age" value="10"/>
    <property name="spouse">
        <bean class="org.springframework.beans.TestBean">
            <property name="age" value="11"/>
        </bean>
    </property>
</bean>

// will result in 11, which is the value of property 'spouse.age' of bean 'person'
<bean id="theAge"
        class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
    <property name="targetBeanName" value="person"/>
    <property name="propertyPath" value="spouse.age"/>
</bean>

在这个例子中,一个路径是针对内部bean进行评估的:

<!-- will result in 12, which is the value of property 'age' of the inner bean -->
<bean id="theAge"
        class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
    <property name="targetObject">
        <bean class="org.springframework.beans.TestBean">
            <property name="age" value="12"/>
        </bean>
    </property>
    <property name="propertyPath" value="age"/>
</bean>

还有一个快捷方式表单,其中bean名称是属性路径。

<!-- will result in 10, which is the value of property 'age' of bean 'person' -->
<bean id="person.age"
        class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

这种形式确实意味着bean的名字中没有选择。 任何对它的引用也必须使用相同的id,这就是路径。 当然,如果用作内部bean,根本不需要参考:

<bean id="..." class="...">
    <property name="age">
        <bean id="person.age"
                class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
    </property>
</bean>

结果类型可以在实际定义中专门设置。 这对于大多数使用情况不是必需的,但可以用于某些情况。 有关此功能的更多信息,请参阅Javadocs。

<util:properties/>

之前…

<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="location" value="classpath:com/foo/jdbc-production.properties"/>
</bean>

上述配置使用Spring FactoryBean实现PropertiesFactoryBean来实例化一个java.util.Properties实例,并使用从所提供的Resource位置加载的值)。

After…​

<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>

<util:list/>

Before…​

<!-- creates a java.util.List instance with values loaded from the supplied 'sourceList' -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
        <list>
            <value>pechorin@hero.org</value>
            <value>raskolnikov@slums.org</value>
            <value>stavrogin@gov.org</value>
            <value>porfiry@gov.org</value>
        </list>
    </property>
</bean>

上述配置使用Spring FactoryBean实现ListFactoryBean创建一个java.util.List实例,该实例使用从提供的sourceList中获取的值进行初始化。

之后…

<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
    <value>pechorin@hero.org</value>
    <value>raskolnikov@slums.org</value>
    <value>stavrogin@gov.org</value>
    <value>porfiry@gov.org</value>
</util:list>

您还可以显式控制将通过使用<util:list />元素上的list-class属性实例化和填充的List的确切类型。 例如,如果我们确实需要一个java.util.LinkedList来实例化,我们可以使用以下配置:

<util:list id="emails" list-class="java.util.LinkedList">
    <value>jackshaftoe@vagabond.org</value>
    <value>eliza@thinkingmanscrumpet.org</value>
    <value>vanhoek@pirate.org</value>
    <value>d'Arcachon@nemesis.org</value>
</util:list>

如果没有提供list-class属性,则容器将选择一个List实现。

<util:map/>

Before…​

<!-- creates a java.util.Map instance with values loaded from the supplied 'sourceMap' -->
<bean id="emails" class="org.springframework.beans.factory.config.MapFactoryBean">
    <property name="sourceMap">
        <map>
            <entry key="pechorin" value="pechorin@hero.org"/>
            <entry key="raskolnikov" value="raskolnikov@slums.org"/>
            <entry key="stavrogin" value="stavrogin@gov.org"/>
            <entry key="porfiry" value="porfiry@gov.org"/>
        </map>
    </property>
</bean>

上述配置使用Spring FactoryBean实现MapFactoryBean创建一个java.util.Map实例,该实例使用从提供的“sourceMap”中获取的键值对进行初始化。

之后…

<!-- creates a java.util.Map instance with the supplied key-value pairs -->
<util:map id="emails">
    <entry key="pechorin" value="pechorin@hero.org"/>
    <entry key="raskolnikov" value="raskolnikov@slums.org"/>
    <entry key="stavrogin" value="stavrogin@gov.org"/>
    <entry key="porfiry" value="porfiry@gov.org"/>
</util:map>

您还可以显式控制将通过在

<util:map id="emails" map-class="java.util.TreeMap">
    <entry key="pechorin" value="pechorin@hero.org"/>
    <entry key="raskolnikov" value="raskolnikov@slums.org"/>
    <entry key="stavrogin" value="stavrogin@gov.org"/>
    <entry key="porfiry" value="porfiry@gov.org"/>
</util:map>

如果没有提供’map-class’属性,则将由容器选择一个Map实现。

<util:set/>

Before…​

<!-- creates a java.util.Set instance with values loaded from the supplied 'sourceSet' -->
<bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean">
    <property name="sourceSet">
        <set>
            <value>pechorin@hero.org</value>
            <value>raskolnikov@slums.org</value>
            <value>stavrogin@gov.org</value>
            <value>porfiry@gov.org</value>
        </set>
    </property>
</bean>

上述配置使用Spring FactoryBean实现SetFactoryBean创建一个java.util.Set实例,该实例使用从提供的“sourceSet”中获取的值进行初始化。

之后…

<!-- creates a java.util.Set instance with the supplied values -->
<util:set id="emails">
    <value>pechorin@hero.org</value>
    <value>raskolnikov@slums.org</value>
    <value>stavrogin@gov.org</value>
    <value>porfiry@gov.org</value>
</util:set>

您还可以显式控制将通过使用<util:set />元素上的’set-class’属性实例化和填充的Set的确切类型。 例如,如果我们确实需要一个java.util.TreeSet来实例化,我们可以使用下面的配置:

<util:set id="emails" set-class="java.util.TreeSet">
    <value>pechorin@hero.org</value>
    <value>raskolnikov@slums.org</value>
    <value>stavrogin@gov.org</value>
    <value>porfiry@gov.org</value>
</util:set>

如果没有提供’set-class’属性,Set实现将由容器选择。

9.1.2. The aop schema

aop标签处理在Spring中配置AOP的所有事情:这包括Spring自己的基于代理的AOP框架和Spring与AspectJ AOP框架的集成。 这些标签在“Spring面向方面编程”一章中全面介绍。

为了完整起见,要在aop模式中使用标签,您需要在Spring XML配置文件的顶部有以下前导码; 以下片段中的文本引用了正确的模式,以便您可以使用aop命名空间中的标签。

<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->

</beans>

9.1.3. The context schema

上下文标签处理与配管相关的ApplicationContext配置 - 也就是说,通常不是对最终用户很重要的bean,而是在Spring中执行大量繁重工作的bean,比如BeanfactoryPostProcessors。 以下片段引用了正确的模式,以便上下文名称空间中的标记可供您使用。

<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->

</beans>

<property-placeholder/>

此元素激活替换$ {…}占位符,并根据指定的属性文件(作为Spring资源位置)进行解析。 这个元素是一个方便的机制,为你设置一个PropertyPlaceholderConfigurer; 如果您需要对PropertyPlaceholderConfigurer进行更多控制,只需明确定义一个。

<annotation-config/>

激活Spring基础结构以便在Bean类中检测各种注释:Spring的@Required和@Autowired,以及JSR 250的@PostConstruct,@PreDestroy和@Resource(如果可用)以及JPA的@PersistenceContext和@PersistenceUnit(如果可用)。 或者,您可以选择为这些注释显式激活单个BeanPostProcessors。

该元素不会激活对Spring的@Transactional注释的处理。 为此目的使用

<component-scan/>

This element is detailed in Annotation-based container configuration.

<load-time-weaver/>

This element is detailed in Load-time weaving with AspectJ in the Spring Framework.

<spring-configured/>

This element is detailed in Using AspectJ to dependency inject domain objects with Spring.

<mbean-export/>

This element is detailed in Configuring annotation based MBean export.

9.1.4. The beans schema

最后但并非最不重要的是,我们在bean模式中有标签。 这些是自框架发布以来一直在Spring中使用的相同标签。 Bean模式中各种标签的例子在这里没有显示,因为它们在Dependencies和配置中有详细的介绍(甚至整个章节)。

请注意,可以向 XML定义添加零个或多个键/值对。 如果有的话,这些额外的元数据完全取决于您自己的定制逻辑(因此,如果您正在编写自己的定制标签,通常只有在标题为XML Schema Authoring的附录中描述)才能使用。

在周围的的上下文中查找标记的示例(请注意,没有任何逻辑来解释它,元数据实际上是毫无用处的)。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="foo" class="x.y.Foo">
        <meta key="cacheName" value="foo"/>
        <property name="name" value="Rick"/>
    </bean>

</beans>

在上面的例子中,你会假设有一些逻辑会消耗bean定义,并使用提供的元数据设置一些缓存基础设施。

9.2. XML Schema Authoring

9.2.1. Introduction

自2.0版以来,Spring已经为用于定义和配置Bean的基本Spring XML格式提供了基于模式的扩展机制。本节将详细介绍如何编写自己的定制XML bean定义分析器并将这些分析器集成到Spring IoC容器中。

为了便于使用模式感知型XML编辑器编写配置文件,Spring的可扩展XML配置机制基于XML模式。如果您不熟悉标准Spring发行版附带的Spring当前XML配置扩展,请首先阅读标题为[xsd-config]的附录。

创建新的XML配置扩展可以通过遵循以下(相对)简单的步骤来完成:

  • 编写XML模式来描述您的自定义元素。

  • 编写自定义NamespaceHandler实现(这是一个简单的步骤,不用担心)。

  • 编写一个或多个BeanDefinitionParser实现(这是实际工作完成的地方)。

  • 用Spring注册上面的工件(这也是一个简单的步骤)。

以下是对这些步骤的描述。 例如,我们将创建一个XML扩展(一个自定义XML元素),使我们能够以简单的方式配置SimpleDateFormat类型的对象(来自java.text包)。 当我们完成后,我们将能够像这样定义SimpleDateFormat类型的bean定义:

<myns:dateformat id="dateFormat"
    pattern="yyyy-MM-dd HH:mm"
    lenient="true"/>

(不要担心这个例子很简单,后面会有更详细的例子,第一个简单例子的意图是引导你完成所涉及的基本步骤。)

9.2.2. Authoring the schema

创建用于Spring IoC容器的XML配置扩展首先创建一个XML Schema来描述扩展。 以下是我们将用于配置SimpleDateFormat对象的模式。

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

(强调的行包含所有可识别标记的扩展基础(意味着它们具有将用作容器中的bean标识符的id属性)我们可以使用此属性,因为我们导入了Spring提供的’ 豆’命名空间。)

上述模式将用于使用<myns:dateformat />元素直接在XML应用程序上下文文件中配置SimpleDateFormat对象。

<myns:dateformat id="dateFormat"
    pattern="yyyy-MM-dd HH:mm"
    lenient="true"/>

请注意,在创建基础结构类之后,上面的XML片段基本上与以下XML片段完全相同。 换句话说,我们只是在容器中创建一个bean,用名为’dateFormat’的SimpleDateFormat类型来标识,并设置了一些属性。

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

基于模式的创建配置格式的方法允许与具有模式感知型XML编辑器的IDE紧密集成。 使用正确的创作模式,您可以使用自动完成让用户在枚举中定义的多个配置选项之间进行选择。

9.2.3. Coding a NamespaceHandler

除了模式之外,我们还需要一个NamespaceHandler来解析Spring解析配置文件时遇到的这个特定命名空间的所有元素。 NamespaceHandler应该在我们的例子中负责解析myns:dateformat元素。

NamespaceHandler接口非常简单,它只有三个方法:

  • init() - 允许初始化NamespaceHandler,并在使用处理程序之前由Spring调用

  • BeanDefinition parse(Element, ParserContext) - 当Spring遇到顶层元素(不嵌套在bean定义或不同的名称空间内)时调用。这个方法可以自己注册bean定义和/或返回一个bean定义。

  • BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext) - 当Spring遇到不同命名空间的属性或嵌套元素时调用。一个或多个bean定义的装饰例如与Spring支持的现成范围一起使用。我们将首先突出一个简单的例子,不使用装饰,之后我们将在更高级的示例中展示装饰。

虽然完全有可能为整个名称空间编写自己的NamespaceHandler(因此提供解析名称空间中每个元素的代码),但Spring XML配置文件中的每个顶级XML元素通常都会导致 一个单一的bean定义(在我们的例子中,单个

package org.springframework.samples.xml;

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

public class MyNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
    }

}

细心的读者会注意到,在这个类中实际上并没有很多解析逻辑。 的确… NamespaceHandlerSupport类具有内置的委托概念。 它支持注册任意数量的BeanDefinitionParser实例,当它需要解析其名称空间中的元素时,它将委派给它。 干净的关注点分离允许NamespaceHandler处理对其名称空间中所有自定义元素进行分析的编排,同时委托BeanDefinitionParsers执行XML解析的繁琐工作; 这意味着每个BeanDefinitionParser将只包含解析单个自定义元素的逻辑,正如我们在下一步中看到的

9.2.4. BeanDefinitionParser

如果NamespaceHandler遇到已映射到特定的bean定义解析器(在本例中为’dateformat’)的XML元素,将使用BeanDefinitionParser。 换句话说,BeanDefinitionParser负责解析模式中定义的一个不同的顶级XML元素。 在解析器中,我们可以访问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 { 

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

}

我们使用Spring提供的AbstractSingleBeanDefinitionParser来处理创建单个BeanDefinition的大量基本工作。

我们为AbstractSingleBeanDefinitionParser超类提供了我们的单个BeanDefinition将代表的类型。

在这种简单的情况下,这就是我们需要做的。 我们的单个BeanDefinition的创建由AbstractSingleBeanDefinitionParser超类来处理,就像提取和设置bean定义的唯一标识符一样。

9.2.5. Registering the handler and the schema

编码完成! 所有仍然需要完成的工作是以某种方式使Spring XML解析基础结构知道我们的自定义元素; 我们通过在两个特殊用途属性文件中注册我们的自定义名称空间处理程序和自定义XSD文件来完成此操作。 这些属性文件都放在应用程序中的’META-INF’目录中,例如,可以与您的二进制类一起分发到JAR文件中。 Spring XML解析基础结构将通过使用这些特殊的属性文件自动选取新的扩展名,这些文件的格式将在下面详细介绍。

‘META-INF/spring.handlers’

名为’spring.handlers’的属性文件包含XML模式URI到名称空间处理程序类的映射。 所以对于我们的例子,我们需要编写以下内容:

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

(’:’字符是Java属性格式中的有效分隔符,因此URI中的’:’字符需要用反斜杠进行转义。)

键 - 值对的第一部分(键)是与您的自定义命名空间扩展相关联的URI,并且需要与您的自定义XSD模式中指定的’targetNamespace’属性的值完全匹配。

‘META-INF/spring.schemas’

名为’spring.schemas’的属性文件包含XML Schema位置(在XML文件中将模式作为“xsi:schemaLocation”属性的一部分引用的架构声明)的映射到类路径资源。 需要该文件来防止Spring绝对必须使用需要Internet访问权限的默认EntityResolver才能检索架构文件。 如果你在这个属性文件中指定映射,Spring将搜索类路径上的模式(在这个例子中’org.springframework.samples.xml’包中的’myns.xsd’):

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

这样做的结果是鼓励您在类路径的NamespaceHandler和BeanDefinitionParser类旁边部署XSD文件。

9.2.6. Using a custom extension in your Spring XML configuration

使用您自己实现的自定义扩展与使用Spring直接提供的“自定义”扩展没有区别。 在Spring XML配置文件中查找使用前面步骤中开发的自定义<dateformat />元素的示例。

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

9.2.7. Meatier examples

在下面查找一些定制XML扩展的更多更好的例子。

Nesting custom tags within custom tags

此示例说明了如何编写满足以下配置目标所需的各种工件:

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

上述配置实际上将自定义扩展嵌套在对方内。 上面的<foo:component />元素实际配置的类是Component类(如下所示)。 注意Component类如何不为’components’属性公开setter方法; 这使得使用setter注入来配置Component类的bean定义变得困难(或者不可能)。

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

}

这个问题的典型解决方案是创建一个自定义的FactoryBean,为“组件”属性公开setter属性。

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

}

这一切都很好,并且很好地工作,但是向最终用户展示了很多Spring管道。 我们要做的是编写一个自定义扩展,以隐藏所有这些Spring管道。 如果我们坚持前面描述的步骤,我们将开始创建XSD架构来定义自定义标签的结构。

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

然后我们将创建一个自定义的NamespaceHandler。

package com.foo;

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

public class ComponentNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("component", new ComponentBeanDefinitionParser());
    }

}

接下来是自定义BeanDefinitionParser。 请记住,我们正在创建一个描述ComponentFactoryBean的BeanDefinition。

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

}

最后,各种工件需要注册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

Custom attributes on ‘normal’ elements

编写自己的自定义分析器和相关的构件并不难,但有时候这不是正确的做法。 考虑您需要将元数据添加到现有的bean定义的场景。 在这种情况下,你当然不希望自己去写自己的整个自定义扩展; 而只是想为现有的bean定义元素添加一个附加属性。

再举一个例子,假设您正在定义服务对象的服务类,该服务对象将(不知道它)访问集群JCache,并且要确保指定的JCache实例在内部急切地启动 周围的群集:

<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"
        jcache:cache-name="checking.account">
    <!-- other dependencies here... -->
</bean>

我们要做的是在解析’jcache:cache-name’属性时创建另一个BeanDefinition; 这个BeanDefinition将为我们初始化命名的JCache。 我们还将修改’checkingAccountService’的现有BeanDefinition,以便它将依赖于这个新的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...
    }

}

现在到自定义扩展。 首先,创建描述自定义属性的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>

接下来,关联的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());
    }

}

接下来,解析器。 请注意,在这种情况下,因为我们要解析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;
    }

}

最后,各种工件需要注册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
阅读更多
文章标签: spring
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭