此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13spring-doc.cn

此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13spring-doc.cn

从 2.0 版本开始, Spring 具有一种将基于 schema 的扩展添加到 用于定义和配置 bean 的基本 Spring XML 格式。本节涵盖 如何编写自己的自定义 XML bean 定义解析器,以及 将此类解析器集成到 Spring IoC 容器中。spring-doc.cn

为了便于编写使用架构感知 XML 编辑器的配置文件, Spring 的可扩展 XML 配置机制基于 XML Schema。如果你不是 熟悉 Spring 标准附带的当前 XML 配置扩展 Spring 发行版,您应该首先阅读上一节 XML 模式spring-doc.cn

要创建新的 XML 配置扩展:spring-doc.cn

  1. 创作 XML 架构以描述您的自定义元素。spring-doc.cn

  2. 编写自定义实现代码。NamespaceHandlerspring-doc.cn

  3. 对一个或多个实现进行编码 (这是完成真正工作的地方)。BeanDefinitionParserspring-doc.cn

  4. 在 Spring 中注册您的新工件。spring-doc.cn

对于一个统一的示例,我们创建一个 XML 扩展名(自定义 XML 元素),它允许我们配置该类型的对象(从包中)。当我们完成时, 我们将能够定义 bean 类型的定义,如下所示:SimpleDateFormatjava.textSimpleDateFormatspring-doc.cn

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

(我们包括更详细的 示例将在本附录的后面部分进行。第一个简单示例的目的是引导您 通过创建自定义扩展的基本步骤。spring-doc.cn

编写架构

创建用于 Spring 的 IoC 容器的 XML 配置扩展的开头为 编写 XML 架构来描述扩展。在我们的示例中,我们使用以下架构 要配置对象:SimpleDateFormatspring-doc.cn

<!-- myns.xsd (inside package org/springframework/samples/xml) -->

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.mycompany.example/schema/myns"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		xmlns:beans="http://www.springframework.org/schema/beans"
		targetNamespace="http://www.mycompany.example/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"> (1)
					<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>
1 指示的行包含所有可识别标记的扩展基础 (这意味着它们有一个属性,我们可以将其用作 容器)。我们可以使用这个属性,因为我们导入了 Spring 提供的名称空间。idbeans

前面的 schema 允许我们直接在 XML 应用程序上下文文件,如下所示 示例显示:SimpleDateFormat<myns:dateformat/>spring-doc.cn

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

请注意,在我们创建了基础结构类之后,前面的 XML 代码片段是 与以下 XML 代码片段基本相同:spring-doc.cn

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

前面两个代码片段中的第二个 在容器中创建一个 bean(由 name of type 标识),并设置了几个属性。dateFormatSimpleDateFormatspring-doc.cn

基于 schema 的创建配置格式的方法允许紧密集成 使用具有架构感知 XML 编辑器的 IDE。通过使用正确编写的架构,您可以 可以使用自动完成让用户在多个配置选项之间进行选择 在枚举中定义。
1 指示的行包含所有可识别标记的扩展基础 (这意味着它们有一个属性,我们可以将其用作 容器)。我们可以使用这个属性,因为我们导入了 Spring 提供的名称空间。idbeans
基于 schema 的创建配置格式的方法允许紧密集成 使用具有架构感知 XML 编辑器的 IDE。通过使用正确编写的架构,您可以 可以使用自动完成让用户在多个配置选项之间进行选择 在枚举中定义。

编码NamespaceHandler

除了 schema 之外,我们还需要一个来解析 Spring 在解析配置文件时遇到的这个特定名称空间。对于此示例,应该负责元素的解析。NamespaceHandlerNamespaceHandlermyns:dateformatspring-doc.cn

该界面有三种方法:NamespaceHandlerspring-doc.cn

  • init():允许初始化 和 由 Spring 之前。NamespaceHandlerspring-doc.cn

  • BeanDefinition parse(Element, ParserContext):当 Spring 遇到 顶级元素(不嵌套在 bean 定义或其他命名空间中)。 此方法本身可以注册 Bean 定义,返回 Bean 定义,或同时返回两者。spring-doc.cn

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

尽管您可以为整个 命名空间(因此提供解析命名空间中每个元素的代码), 通常情况下,Spring XML 配置文件中的每个顶级 XML 元素 生成单个 bean 定义(就像我们的例子中,单个元素导致单个 bean 定义)。Spring 具有 支持此方案的便利类的数量。在下面的示例中,我们 使用类:NamespaceHandler<myns:dateformat/>SimpleDateFormatNamespaceHandlerSupportspring-doc.cn

package org.springframework.samples.xml;

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

public class MyNamespaceHandler extends NamespaceHandlerSupport {

	public void init() {
		registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
	}
}
package org.springframework.samples.xml

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

class MyNamespaceHandler : NamespaceHandlerSupport {

	override fun init() {
		registerBeanDefinitionParser("dateformat", SimpleDateFormatBeanDefinitionParser())
	}
}

您可能会注意到,实际上并没有大量的解析逻辑 在这个类中。事实上,该类具有 代表团。它支持注册任意数量的实例,当需要解析其 命名空间。这种清晰的关注点分离让 编排其命名空间中所有自定义元素的解析,而 委托 to 执行 XML 解析的繁重工作。这 表示每个 API 仅包含解析单个 custom 元素,正如我们在下一步中看到的那样。NamespaceHandlerSupportBeanDefinitionParserNamespaceHandlerBeanDefinitionParsersBeanDefinitionParserspring-doc.cn

BeanDefinitionParser

如果遇到 XML,则使用 A 元素,该元素已映射到特定 Bean 定义解析器 (在本例中)。换句话说, is 负责解析架构中定义的一个不同的顶级 XML 元素。在 解析器中,我们可以访问 XML 元素(因此也可以访问它的子元素),以便 我们可以解析自定义 XML 内容,如以下示例所示:BeanDefinitionParserNamespaceHandlerdateformatBeanDefinitionParserspring-doc.cn

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.addConstructorArgValue(pattern);

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

}
1 我们使用 Spring 提供的 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供 single 代表。AbstractSingleBeanDefinitionParserBeanDefinition
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

class SimpleDateFormatBeanDefinitionParser : AbstractSingleBeanDefinitionParser() { (1)

	override fun getBeanClass(element: Element): Class<*>? { (2)
		return SimpleDateFormat::class.java
	}

	override fun doParse(element: Element, bean: BeanDefinitionBuilder) {
		// this will never be null since the schema explicitly requires that a value be supplied
		val pattern = element.getAttribute("pattern")
		bean.addConstructorArgValue(pattern)

		// this however is an optional property
		val lenient = element.getAttribute("lenient")
		if (StringUtils.hasText(lenient)) {
			bean.addPropertyValue("lenient", java.lang.Boolean.valueOf(lenient))
		}
	}
}
1 我们使用 Spring 提供的 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供 single 代表。AbstractSingleBeanDefinitionParserBeanDefinition

在这个简单的情况下,这就是我们需要做的全部。我们的 single 的创建由超类处理,因为 是 Bean 定义的唯一标识符的提取和设置。BeanDefinitionAbstractSingleBeanDefinitionParserspring-doc.cn

1 我们使用 Spring 提供的 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供 single 代表。AbstractSingleBeanDefinitionParserBeanDefinition
1 我们使用 Spring 提供的 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供 single 代表。AbstractSingleBeanDefinitionParserBeanDefinition

注册处理程序和 Schema

编码完成。剩下要做的就是创建 Spring XML 解析基础设施感知我们的自定义元素。为此,我们在两个特殊用途的属性文件中注册了自定义和自定义 XSD 文件。这些 属性文件都放置在应用程序的目录中,并且 例如,可以与二进制类一起在 JAR 文件中分发。Spring XML 解析基础设施通过使用 这些特殊属性文件,其格式将在接下来的两节中详细介绍。namespaceHandlerMETA-INFspring-doc.cn

写作META-INF/spring.handlers

调用的属性文件包含 XML 架构 URI 到 命名空间处理程序类。对于我们的示例,我们需要编写以下内容:spring.handlersspring-doc.cn

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

(该字符是 Java 属性格式中的有效分隔符,因此 URI 中的字符需要使用反斜杠进行转义。::spring-doc.cn

键值对的第一部分(键)是与您的自定义关联的 URI 命名空间扩展名,并且需要与自定义 XSD 架构中指定的属性值完全匹配。targetNamespacespring-doc.cn

编写 'META-INF/spring.schemas'

调用的属性文件包含 XML 架构位置的映射 (与架构声明一起引用,在使用架构作为一部分的 XML 文件中 )添加到 Classpath 资源中。此文件是必需的 以防止 Spring 绝对必须使用 require 用于检索架构文件的 Internet 访问。如果您在此 properties 文件,Spring 在 Classpath 上搜索模式(在本例中,在包中)。 以下代码片段显示了我们需要为自定义架构添加的行:spring.schemasxsi:schemaLocationEntityResolvermyns.xsdorg.springframework.samples.xmlspring-doc.cn

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

(请记住,字符必须转义。:spring-doc.cn

我们鼓励您同时部署 XSD 文件 类路径上的 and 类。NamespaceHandlerBeanDefinitionParserspring-doc.cn

在 Spring XML 配置中使用自定义扩展

使用您自己实现的自定义扩展与使用 Spring 提供的“自定义”扩展之一。以下内容 example 使用前面步骤中开发的自定义元素 在 Spring XML 配置文件中:<dateformat/>spring-doc.cn

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

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

</beans>
1 我们的定制 bean。
1 我们的定制 bean。

更详细的示例

本节提供了一些更详细的自定义 XML 扩展示例。spring-doc.cn

在自定义元素中嵌套自定义元素

本节中介绍的示例显示了如何编写所需的各种工件 满足以下配置的目标:spring-doc.cn

<?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.example/schema/component"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.foo.example/schema/component http://www.foo.example/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>

前面的配置将自定义扩展相互嵌套。类 )实际上由 Element 配置的是类(如下一个示例所示)。请注意该类如何不公开 setter 方法。这使得它变得困难(或者说是不可能的) 使用 setter 注入为类配置 Bean 定义。 下面的清单显示了该类:<foo:component/>ComponentComponentcomponentsComponentComponentspring-doc.cn

package com.foo;

import java.util.ArrayList;
import java.util.List;

public class Component {

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

	// 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;
	}
}
package com.foo

import java.util.ArrayList

class Component {

	var name: String? = null
	private val components = ArrayList<Component>()

	// there is no setter method for the 'components'
	fun addComponent(component: Component) {
		this.components.add(component)
	}

	fun getComponents(): List<Component> {
		return components
	}
}

此问题的典型解决方案是创建一个自定义,该自定义将 setter 属性。下面的清单显示了这样的自定义:FactoryBeancomponentsFactoryBeanspring-doc.cn

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;
	}
}
package com.foo

import org.springframework.beans.factory.FactoryBean
import org.springframework.stereotype.Component

class ComponentFactoryBean : FactoryBean<Component> {

	private var parent: Component? = null
	private var children: List<Component>? = null

	fun setParent(parent: Component) {
		this.parent = parent
	}

	fun setChildren(children: List<Component>) {
		this.children = children
	}

	override fun getObject(): Component? {
		if (this.children != null && this.children!!.isNotEmpty()) {
			for (child in children!!) {
				this.parent!!.addComponent(child)
			}
		}
		return this.parent
	}

	override fun getObjectType(): Class<Component>? {
		return Component::class.java
	}

	override fun isSingleton(): Boolean {
		return true
	}
}

这工作得很好,但它向最终用户公开了大量的 Spring 管道。我们是什么 要做的是编写一个自定义扩展,隐藏所有这些 Spring 管道。 如果我们坚持前面描述的步骤,我们就会从 通过创建 XSD 架构来定义自定义标签的结构,如下所示 列表显示:spring-doc.cn

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

<xsd:schema xmlns="http://www.foo.example/schema/component"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		targetNamespace="http://www.foo.example/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>

再次按照前面描述的过程, 然后我们创建一个自定义 :NamespaceHandlerspring-doc.cn

package com.foo;

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

public class ComponentNamespaceHandler extends NamespaceHandlerSupport {

	public void init() {
		registerBeanDefinitionParser("component", new ComponentBeanDefinitionParser());
	}
}
package com.foo

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

class ComponentNamespaceHandler : NamespaceHandlerSupport() {

	override fun init() {
		registerBeanDefinitionParser("component", ComponentBeanDefinitionParser())
	}
}

接下来是自定义 .请记住,我们正在创建 a 描述 .以下内容 清单 显示了我们的自定义实现:BeanDefinitionParserBeanDefinitionComponentFactoryBeanBeanDefinitionParserspring-doc.cn

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<>(childElements.size());
		for (Element element : childElements) {
			children.add(parseComponentElement(element));
		}
		factory.addPropertyValue("children", children);
	}
}
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

class ComponentBeanDefinitionParser : AbstractBeanDefinitionParser() {

	override fun parseInternal(element: Element, parserContext: ParserContext): AbstractBeanDefinition? {
		return parseComponentElement(element)
	}

	private fun parseComponentElement(element: Element): AbstractBeanDefinition {
		val factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean::class.java)
		factory.addPropertyValue("parent", parseComponent(element))

		val childElements = DomUtils.getChildElementsByTagName(element, "component")
		if (childElements != null && childElements.size > 0) {
			parseChildComponents(childElements, factory)
		}

		return factory.getBeanDefinition()
	}

	private fun parseComponent(element: Element): BeanDefinition {
		val component = BeanDefinitionBuilder.rootBeanDefinition(Component::class.java)
		component.addPropertyValue("name", element.getAttribute("name"))
		return component.beanDefinition
	}

	private fun parseChildComponents(childElements: List<Element>, factory: BeanDefinitionBuilder) {
		val children = ManagedList<BeanDefinition>(childElements.size)
		for (element in childElements) {
			children.add(parseComponentElement(element))
		}
		factory.addPropertyValue("children", children)
	}
}

最后,各种工件需要注册到 Spring XML 基础设施中。 通过修改 AND 文件,如下所示:META-INF/spring.handlersMETA-INF/spring.schemasspring-doc.cn

# in 'META-INF/spring.handlers'
http\://www.foo.example/schema/component=com.foo.ComponentNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.example/schema/component/component.xsd=com/foo/component.xsd

“Normal” 元素上的自定义属性

编写自己的自定义解析器和相关工件并不难。然而 有时这不是正确的做法。考虑一个场景,您需要 将元数据添加到现有的 Bean 定义中。在这种情况下,您当然 不想编写自己的整个自定义扩展。相反,你只是 希望向现有的 Bean 定义元素添加一个附加属性。spring-doc.cn

再举一个例子,假设你为 service 对象(它不知道)访问集群 JCache,并且您希望确保 命名的 JCache 实例在周围的集群中急切地启动。 下面的清单显示了这样的定义:spring-doc.cn

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

然后,我们可以在解析 attribute 时创建另一个。然后初始化 为我们命名的 JCache。我们还可以修改 existing for ,以便它依赖于这个新的 JCache 初始化。以下列表显示了我们的 :BeanDefinition'jcache:cache-name'BeanDefinitionBeanDefinition'checkingAccountService'BeanDefinitionJCacheInitializerspring-doc.cn

package com.foo;

public class JCacheInitializer {

	private final String name;

	public JCacheInitializer(String name) {
		this.name = name;
	}

	public void initialize() {
		// lots of JCache API calls to initialize the named cache...
	}
}
package com.foo

class JCacheInitializer(private val name: String) {

	fun initialize() {
		// lots of JCache API calls to initialize the named cache...
	}
}

现在我们可以转到自定义扩展。首先,我们需要编写 描述 custom 属性的 XSD 架构,如下所示:spring-doc.cn

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

<xsd:schema xmlns="http://www.foo.example/schema/jcache"
		xmlns:xsd="http://www.w3.org/2001/XMLSchema"
		targetNamespace="http://www.foo.example/schema/jcache"
		elementFormDefault="qualified">

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

</xsd:schema>

接下来,我们需要创建关联的 ,如下所示:NamespaceHandlerspring-doc.cn

package com.foo;

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

public class JCacheNamespaceHandler extends NamespaceHandlerSupport {

	public void init() {
		super.registerBeanDefinitionDecoratorForAttribute("cache-name",
			new JCacheInitializingBeanDefinitionDecorator());
	}

}
package com.foo

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

class JCacheNamespaceHandler : NamespaceHandlerSupport() {

	override fun init() {
		super.registerBeanDefinitionDecoratorForAttribute("cache-name",
				JCacheInitializingBeanDefinitionDecorator())
	}

}

接下来,我们需要创建解析器。请注意,在本例中,因为我们要解析 一个 XML 属性,我们编写 a 而不是 . 下面的清单显示了我们的实现:BeanDefinitionDecoratorBeanDefinitionParserBeanDefinitionDecoratorspring-doc.cn

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

class JCacheInitializingBeanDefinitionDecorator : BeanDefinitionDecorator {

	override fun decorate(source: Node, holder: BeanDefinitionHolder,
						ctx: ParserContext): BeanDefinitionHolder {
		val initializerBeanName = registerJCacheInitializer(source, ctx)
		createDependencyOnJCacheInitializer(holder, initializerBeanName)
		return holder
	}

	private fun createDependencyOnJCacheInitializer(holder: BeanDefinitionHolder,
													initializerBeanName: String) {
		val definition = holder.beanDefinition as AbstractBeanDefinition
		var dependsOn = definition.dependsOn
		dependsOn = if (dependsOn == null) {
			arrayOf(initializerBeanName)
		} else {
			val dependencies = ArrayList(listOf(*dependsOn))
			dependencies.add(initializerBeanName)
			dependencies.toTypedArray()
		}
		definition.setDependsOn(*dependsOn)
	}

	private fun registerJCacheInitializer(source: Node, ctx: ParserContext): String {
		val cacheName = (source as Attr).value
		val beanName = "$cacheName-initializer"
		if (!ctx.registry.containsBeanDefinition(beanName)) {
			val initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer::class.java)
			initializer.addConstructorArg(cacheName)
			ctx.registry.registerBeanDefinition(beanName, initializer.getBeanDefinition())
		}
		return beanName
	}
}

最后,我们需要向 Spring XML 基础架构注册各种工件 通过修改 AND 文件,如下所示:META-INF/spring.handlersMETA-INF/spring.schemasspring-doc.cn

# in 'META-INF/spring.handlers'
http\://www.foo.example/schema/jcache=com.foo.JCacheNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.example/schema/jcache/jcache.xsd=com/foo/jcache.xsd