对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
XML 架构创作
从 2.0 版本开始, Spring 具有一种将基于 schema 的扩展添加到 用于定义和配置 bean 的基本 Spring XML 格式。本节涵盖 如何编写自己的自定义 XML bean 定义解析器,以及 将此类解析器集成到 Spring IoC 容器中。
为了便于编写使用架构感知 XML 编辑器的配置文件, Spring 的可扩展 XML 配置机制基于 XML Schema。如果你不是 熟悉 Spring 标准附带的当前 XML 配置扩展 Spring 发行版,您应该首先阅读上一节 XML 模式。
要创建新的 XML 配置扩展:
-
创作 XML 架构以描述您的自定义元素。
-
编写自定义代码
NamespaceHandler
实现。 -
对一个或多个进行编码
BeanDefinitionParser
实现 (这是完成真正工作的地方)。 -
在 Spring 中注册您的新工件。
对于一个统一的示例,我们创建一个
XML 扩展名(自定义 XML 元素),允许我们配置以下类型的对象SimpleDateFormat
(来自java.text
包)。当我们完成时,
我们将能够定义 bean 类型的定义SimpleDateFormat
如下:
<myns:dateformat id="dateFormat"
pattern="yyyy-MM-dd HH:mm"
lenient="true"/>
(我们包括更详细的 示例将在本附录的后面部分进行。第一个简单示例的目的是引导您 通过创建自定义扩展的基本步骤。
编写架构
创建用于 Spring 的 IoC 容器的 XML 配置扩展的开头为
编写 XML 架构来描述扩展。在我们的示例中,我们使用以下架构
配置SimpleDateFormat
对象:
<!-- 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 | 指示的行包含所有可识别标记的扩展基础
(意味着他们有一个id 属性,我们可以将其用作
容器)。我们可以使用这个属性,因为我们导入了 Spring 提供的beans Namespace。 |
前面的 schema 允许我们配置SimpleDateFormat
对象直接放在
XML 应用程序上下文文件。<myns:dateformat/>
元素,如下所示
示例显示:
<myns:dateformat id="dateFormat"
pattern="yyyy-MM-dd HH:mm"
lenient="true"/>
请注意,在我们创建了基础结构类之后,前面的 XML 代码片段是 与以下 XML 代码片段基本相同:
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd HH:mm"/>
<property name="lenient" value="true"/>
</bean>
前面两个代码片段中的第二个
在容器中创建一个 Bean(由名称dateFormat
的类型SimpleDateFormat
) 设置了几个属性。
基于 schema 的创建配置格式的方法允许紧密集成 使用具有架构感知 XML 编辑器的 IDE。通过使用正确编写的架构,您可以 可以使用自动完成让用户在多个配置选项之间进行选择 在枚举中定义。 |
编码NamespaceHandler
除了 schema 之外,我们还需要一个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 支持的范围一起使用。 我们首先突出显示一个简单的示例,不使用装饰,然后 我们在一个更高级的例子中展示 Decoration。
虽然您可以编写自己的代码NamespaceHandler
对于整个
命名空间(因此提供解析命名空间中每个元素的代码),
通常情况下,Spring XML 配置文件中的每个顶级 XML 元素
结果会得到一个 bean 定义(就像我们的例子中一样,其中单个<myns:dateformat/>
元素会生成单个SimpleDateFormat
bean 定义)。Spring 具有
支持此方案的便利类的数量。在下面的示例中,我们
使用NamespaceHandlerSupport
类:
-
Java
-
Kotlin
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())
}
}
您可能会注意到,实际上并没有大量的解析逻辑
在这个类中。事实上,NamespaceHandlerSupport
class 有一个内置的
代表团。它支持注册任意数量的BeanDefinitionParser
实例,当需要解析其
Namespace。这种清晰的关注点分离使NamespaceHandler
处理
编排其命名空间中所有自定义元素的解析,而
委托给BeanDefinitionParsers
来执行 XML 解析的繁重工作。这
表示每个BeanDefinitionParser
仅包含用于解析单个
custom 元素,正如我们在下一步中看到的那样。
用BeanDefinitionParser
一个BeanDefinitionParser
如果NamespaceHandler
遇到 XML
元素,该元素已映射到特定 Bean 定义解析器
(dateformat
在本例中)。换句话说,BeanDefinitionParser
是
负责解析架构中定义的一个不同的顶级 XML 元素。在
解析器中,我们可以访问 XML 元素(因此也可以访问它的子元素),以便
我们可以解析自定义 XML 内容,如以下示例所示:
-
Java
-
Kotlin
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 提供的AbstractSingleBeanDefinitionParser 处理大量
创建单个BeanDefinition . |
2 | 我们提供AbstractSingleBeanDefinitionParser superclass 替换为我们的
单BeanDefinition 代表。 |
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 提供的AbstractSingleBeanDefinitionParser 处理大量
创建单个BeanDefinition . |
2 | 我们提供AbstractSingleBeanDefinitionParser superclass 替换为我们的
单BeanDefinition 代表。 |
在这个简单的情况下,这就是我们需要做的全部。我们单曲的创建BeanDefinition
由AbstractSingleBeanDefinitionParser
superclass 的
是 Bean 定义的唯一标识符的提取和设置。
注册处理程序和 Schema
编码完成。剩下要做的就是创建 Spring XML
解析基础设施感知我们的自定义元素。我们通过注册我们的自定义来做到这一点namespaceHandler
和自定义 XSD 文件。这些
属性文件都放置在META-INF
目录中的目录,并将
例如,可以与二进制类一起在 JAR 文件中分发。Spring
XML 解析基础设施通过使用
这些特殊属性文件,其格式将在接下来的两节中详细介绍。
写作META-INF/spring.handlers
名为spring.handlers
包含 XML 架构 URI 到
命名空间处理程序类。对于我们的示例,我们需要编写以下内容:
http\://www.mycompany.example/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
(该:
character 是 Java 属性格式中的有效分隔符,因此:
字符需要使用反斜杠进行转义。
键值对的第一部分(键)是与您的自定义关联的 URI
namespace 扩展名,并且需要与targetNamespace
属性,如自定义 XSD 架构中所指定。
编写 'META-INF/spring.schemas'
名为spring.schemas
包含 XML 架构位置的映射
(与架构声明一起引用,在使用架构作为一部分的 XML 文件中
的xsi:schemaLocation
属性)添加到 Classpath 资源中。此文件是必需的
以防止 Spring 绝对必须使用默认的EntityResolver
这需要
用于检索架构文件的 Internet 访问。如果您在此
properties 文件中,Spring 会搜索 schema(在本例中为myns.xsd
在org.springframework.samples.xml
package) 的 API 中。
以下代码片段显示了我们需要为自定义架构添加的行:
http\://www.mycompany.example/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
(请记住,:
字符必须转义。
我们鼓励您同时部署 XSD 文件
这NamespaceHandler
和BeanDefinitionParser
类。
在 Spring XML 配置中使用自定义扩展
使用您自己实现的自定义扩展与使用
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.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。 |
更详细的示例
本节提供了一些更详细的自定义 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: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>
前面的配置将自定义扩展相互嵌套。类
,实际上是由<foo:component/>
元素是Component
类(如下一个示例所示)。请注意Component
类不会公开
setter 方法的components
财产。这使得它变得困难(或者说是不可能的)
要为Component
类。
下面的清单显示了Component
类:
-
Java
-
Kotlin
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
}
}
此问题的典型解决方案是创建自定义FactoryBean
这会暴露一个
setter 属性的components
财产。下面的清单显示了这样的自定义FactoryBean
:
-
Java
-
Kotlin
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 架构来定义自定义标签的结构,如下所示 列表显示:
<?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>
再次按照前面描述的过程,
然后,我们创建一个自定义NamespaceHandler
:
-
Java
-
Kotlin
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())
}
}
接下来是自定义BeanDefinitionParser
.请记住,我们正在创建
一个BeanDefinition
,它描述了一个ComponentFactoryBean
.以下内容
列表显示我们的自定义BeanDefinitionParser
实现:
-
Java
-
Kotlin
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 基础设施中。
通过修改META-INF/spring.handlers
和META-INF/spring.schemas
文件,如下所示:
# 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 定义元素添加一个附加属性。
再举一个例子,假设你为 service 对象(它不知道)访问集群 JCache,并且您希望确保 命名的 JCache 实例在周围的集群中急切地启动。 下面的清单显示了这样的定义:
<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"
jcache:cache-name="checking.account">
<!-- other dependencies here... -->
</bean>
然后我们可以创建另一个BeanDefinition
当'jcache:cache-name'
属性。这BeanDefinition
然后初始化
为我们命名的 JCache。我们还可以修改现有的BeanDefinition
对于'checkingAccountService'
,因此它依赖于这个新的
JCache 初始化BeanDefinition
.以下清单显示了我们的JCacheInitializer
:
-
Java
-
Kotlin
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 架构,如下所示:
<?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>
接下来,我们需要创建关联的NamespaceHandler
如下:
-
Java
-
Kotlin
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 属性,我们编写一个BeanDefinitionDecorator
而不是BeanDefinitionParser
.
以下清单显示了我们的BeanDefinitionDecorator
实现:
-
Java
-
Kotlin
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 基础架构注册各种工件
通过修改META-INF/spring.handlers
和META-INF/spring.schemas
文件,如下所示:
# 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