此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.1.10Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.1.10Spring中文文档

从 2.0 版本开始,Spring 就采用了一种机制,用于将基于模式的扩展添加到 用于定义和配置 Bean 的基本 Spring XML 格式。本节涵盖 如何编写自己的自定义 XML Bean 定义解析器和 将此类解析器集成到 Spring IoC 容器中。Spring中文文档

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

要创建新的 XML 配置扩展,请执行以下操作:Spring中文文档

  1. 创作 XML 架构以描述自定义元素。Spring中文文档

  2. 编写自定义实现代码。NamespaceHandlerSpring中文文档

  3. 编写一个或多个实现代码 (这是完成真正工作的地方)。BeanDefinitionParserSpring中文文档

  4. 在 Spring 中注册您的新工件。Spring中文文档

对于一个统一的例子,我们创建一个 XML 扩展(自定义 XML 元素),允许我们配置类型(来自包)的对象。当我们完成时, 我们将能够定义 Bean 类型的定义,如下所示:SimpleDateFormatjava.textSimpleDateFormatSpring中文文档

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

(我们包括更详细的内容 示例见本附录后面部分。第一个简单示例的目的是引导您 通过制作自定义扩展的基本步骤。Spring中文文档

创作架构

创建用于 Spring 的 IoC 容器的 XML 配置扩展时,首先要 创作 XML 架构以描述扩展。对于我们的示例,我们使用以下架构 要配置对象:SimpleDateFormatSpring中文文档

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

前面的架构允许我们直接在 XML 应用程序上下文文件,如下所示 示例显示:SimpleDateFormat<myns:dateformat/>Spring中文文档

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

请注意,在我们创建基础结构类之后,前面的 XML 代码段是 本质上与以下 XML 代码段相同:Spring中文文档

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

前面两个代码段中的第二个 在容器中创建一个 Bean(由 type 名称标识),并设置了几个属性。dateFormatSimpleDateFormatSpring中文文档

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

编码NamespaceHandler

除了 schema 之外,我们还需要 a 来解析 Spring 在解析配置文件时遇到的这个特定命名空间。在此示例中,应负责元素的解析。NamespaceHandlerNamespaceHandlermyns:dateformatSpring中文文档

该界面具有三种方法:NamespaceHandlerSpring中文文档

  • init():允许初始化 和 被调用 使用处理程序前的弹簧。NamespaceHandlerSpring中文文档

  • BeanDefinition parse(Element, ParserContext):当Spring遇到 顶级元素(未嵌套在 Bean 定义或其他命名空间中)。 此方法本身可以注册 Bean 定义和/或返回 Bean 定义。Spring中文文档

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

虽然你可以为整个编写自己的代码 命名空间(因此提供解析命名空间中每个元素的代码), 通常情况下,Spring XML配置文件中的每个顶级XML元素 生成单个 Bean 定义(如我们的例子,其中单个元素生成单个 Bean 定义)。Spring 具有 支持此方案的便利类数。在下面的例子中,我们 使用类:NamespaceHandler<myns:dateformat/>SimpleDateFormatNamespaceHandlerSupportSpring中文文档

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

您可能会注意到,实际上并没有很多解析逻辑 在这堂课上。事实上,这个类有一个内在的概念 代表团。它支持注册任意数量的实例,当它需要解析其 命名空间。这种干净的关注点分离让处理 编排其命名空间中所有自定义元素的解析,而 委托来执行 XML 解析的繁重工作。这 意味着每个都只包含用于解析单个的逻辑 自定义元素,正如我们在下一步中看到的那样。NamespaceHandlerSupportBeanDefinitionParserNamespaceHandlerBeanDefinitionParsersBeanDefinitionParserSpring中文文档

BeanDefinitionParser

如果遇到 XML 已映射到特定 Bean 定义解析器的类型的元素 (在本例中)。换句话说,是 负责分析架构中定义的一个不同的顶级 XML 元素。在 解析器,我们可以访问 XML 元素(因此也可以访问其子元素),以便 我们可以解析自定义 XML 内容,如以下示例所示:BeanDefinitionParserNamespaceHandlerdateformatBeanDefinitionParserSpring中文文档

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 提供的 Spring来处理很多 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供我们的 单身代表。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 提供的 Spring来处理很多 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供我们的 单身代表。AbstractSingleBeanDefinitionParserBeanDefinition

在这个简单的情况下,这就是我们需要做的。我们单曲的创建由超类处理,如 是 Bean 定义的唯一标识符的提取和设置。BeanDefinitionAbstractSingleBeanDefinitionParserSpring中文文档

1 我们使用 Spring 提供的 Spring来处理很多 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供我们的 单身代表。AbstractSingleBeanDefinitionParserBeanDefinition
1 我们使用 Spring 提供的 Spring来处理很多 创建单个 .AbstractSingleBeanDefinitionParserBeanDefinition
2 我们为超类提供我们的 单身代表。AbstractSingleBeanDefinitionParserBeanDefinition

注册处理程序和架构

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

写作META-INF/spring.handlers

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

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

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

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

编写“META-INF/spring.schemas”

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

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

(请记住,必须对角色进行转义。:Spring中文文档

我们鼓励您部署您的 XSD 文件(或文件) 类路径上的 and 类。NamespaceHandlerBeanDefinitionParserSpring中文文档

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

使用您自己实现的自定义扩展与使用 Spring 提供的“自定义”扩展之一。以下 示例使用在前面步骤中开发的自定义元素 在 Spring XML 配置文件中:<dateformat/>Spring中文文档

<?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 我们的定制豆。
1 我们的定制豆。

更详细的例子

本节介绍一些更详细的自定义 XML 扩展示例。Spring中文文档

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

本节中提供的示例演示如何编写所需的各种工件 满足以下配置的目标:Spring中文文档

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

上述配置将自定义扩展嵌套在彼此之间。班级 该元素实际上是由类配置的(在下一个示例中显示)。请注意,该类不会公开 属性的 setter 方法。这使得它很难(或者更确切地说是不可能的) 使用 setter 注入为类配置 Bean 定义。 以下列表显示了该类:<foo:component/>ComponentComponentcomponentsComponentComponentSpring中文文档

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中文文档

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中文文档

<?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中文文档

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

接下来是自定义。请记住,我们正在创造 描述 .以下 列表显示了我们的自定义实现:BeanDefinitionParserBeanDefinitionComponentFactoryBeanBeanDefinitionParserSpring中文文档

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中文文档

# 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

“普通”元素上的自定义属性

编写自己的自定义解析器和关联的项目并不难。然而 有时这不是正确的做法。考虑一个需要 将元数据添加到现有的 Bean 定义中。在这种情况下,你当然 不想编写自己的整个自定义扩展。相反,你只是 想要向现有 Bean 定义元素添加其他属性。Spring中文文档

举另一个例子,假设你为 服务对象(它不知道)访问一个集群 JCache,并且您希望确保 命名的 JCache 实例在周围的集群中急切地启动。 以下列表显示了这样的定义:Spring中文文档

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

然后,我们可以在解析属性时创建另一个属性。然后初始化 为我们命名的 JCache。我们还可以修改现有的 for ,使其依赖于这个新的 JCache 初始化 .以下列表显示了我们的:BeanDefinition'jcache:cache-name'BeanDefinitionBeanDefinition'checkingAccountService'BeanDefinitionJCacheInitializerSpring中文文档

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

现在我们可以转到自定义扩展。首先,我们需要创作 描述自定义属性的 XSD 架构,如下所示:Spring中文文档

<?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中文文档

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 属性,我们写一个而不是一个 . 以下列表显示了我们的实现:BeanDefinitionDecoratorBeanDefinitionParserBeanDefinitionDecoratorSpring中文文档

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中文文档

# 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