|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
XML 模式创作
自2.0版本起,Spring提供了一种机制,可以为 用于定义和配置豆子的基本 Spring XML 格式。本节内容涵盖 如何编写自定义XML豆定义解析器和 将这些解析器集成到 Spring IoC 容器中。
为了方便使用模式识别XML编辑器的配置文件, Spring 的可扩展 XML 配置机制基于 XML Schema。如果你不喜欢 熟悉Spring当前标准自带的XML配置扩展 关于Spring发行版,你应该先阅读之前关于XML Schema的部分。
要创建新的 XML 配置扩展:
举一个统一的例子,我们创建一个
XML 扩展(自定义 XML 元素),允许我们配置 以下类型的对象SimpleDate格式(摘自java.textpackage)。等我们结束后,
我们将能够定义类型的Beans定义SimpleDate格式如下:
<myns:dateformat id="dateFormat"
pattern="yyyy-MM-dd HH:mm"
lenient="true"/>
(我们包含了更详细的内容 后续附录中将举例说明。这个第一个简单例子的目的是让你走路 通过制作自定义扩展的基本步骤。)
模式的编写
创建用于 Spring IoC 容器的 XML 配置扩展时,首先是
编写一个XML模式来描述扩展。在我们的例子中,我们使用以下模式
配置SimpleDate格式对象:
<!-- 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 提供的豆Namespace。 |
前面的模式让我们可以配置SimpleDate格式直接在
通过使用<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>
前两个片段中的第二个
在容器中生成豆子(以名称识别)日期格式类型SimpleDate格式)并设置了几个属性。
| 基于模式的配置格式创建方法允许紧密集成 该集成开发环境(IDE)具有模式识别的XML编辑器。通过使用正确编写的模式,你 可以使用自动补全功能,让用户在多个配置选项中选择 定义在枚举中。 |
编码aNamespaceHandler
除了模式外,我们还需要一个NamespaceHandler解析所有元素
这是 Spring 在解析配置文件时遇到的特定命名空间。在这个例子中,NamespaceHandler应该负责解析Myns:Dateformat元素。
这NamespaceHandler界面具有三种方法:
-
init():允许初始化NamespaceHandler且称为 在使用作器之前先Spring。 -
BeanDefinition 解析法(Element, ParserContext):当Spring遇到 顶层元素(不嵌套在 bean 定义或其他命名空间中)。 该方法本身可以注册豆定义,返回豆定义,或两者兼具。 -
BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext):叫 当 Spring 遇到不同命名空间的属性或嵌套元素时。 例如,Spring支持的示波器会装饰一个或多个Beans定义。 我们先用一个简单的例子,不使用装饰,然后再强调 我们展示了一个稍微高级的例子。
虽然你可以自己写代码NamespaceHandler整个
命名空间(因此提供解析命名空间中每个元素的代码),
通常情况下,Spring XML 配置文件中的每个顶层 XML 元素
结果是单豆定义(如我们所见,单一<myns:dateformat/>元素 结果为单一SimpleDate格式豆子定义)。春季有
支持这种情景的便利类数量。在下面的例子中,我们
使用以下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类本身就有
代表团。它支持任意数量的注册BeanDefinition解析器实例,当需要解析其元素时,它会委派给这些实例
Namespace。这种清晰的关注点分离使得NamespaceHandler处理
对命名空间中所有自定义元素的解析进行编排,同时
委派至BeanDefinition解析器负责 XML 解析的繁重工作。这
意味着每个BeanDefinition解析器仅包含解析单个
自定义元素,正如我们在下一步中看到的。
用BeanDefinition解析器
一个BeanDefinition解析器如果NamespaceHandler遇到一个XML
该类型的元素被映射到特定的 Bean 定义解析器
(日期格式在这里)。换句话说,BeanDefinition解析器是
负责解析模式中定义的一个独立顶层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提供的摘要SingleBeanDefinition解析器要处理很多
制作单一的基础繁重工作豆子定义. |
| 2 | 我们提供摘要SingleBeanDefinition解析器具有 我们的类型
单豆子定义代表。 |
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提供的摘要SingleBeanDefinition解析器要处理很多
制作单一的基础繁重工作豆子定义. |
| 2 | 我们提供摘要SingleBeanDefinition解析器具有 我们的类型
单豆子定义代表。 |
在这个简单的例子中,这就是我们所需要做的全部。我们单曲的诞生豆子定义由摘要SingleBeanDefinition解析器超类,作为
是提取并设置豆子定义的唯一标识符。
注册处理器和模式
编码完成了。剩下的就是制作 Spring XML
解析识别我们自定义元素的基础设施。我们通过注册我们的客户来实现namespaceHandler以及两个专用属性文件中的自定义 XSD 文件。这些
属性文件都被放置在元步兵应用程序中的目录 和
例如,可以与二进制类一起分发在JAR文件中。Spring
XML 解析基础设施会自动通过消耗
这些特殊属性文件的格式将在接下来的两节详细说明。
写作META-INF/spring.handlers
属性文件称为Spring。控者包含XML模式URI映射到
命名空间处理类。对于我们的例子,我们需要写以下内容:
http\://www.mycompany.example/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
(:字符在 Java 属性格式中是有效的分隔符,因此:URI中的字符需要用反斜线逃脱。)
键值对的第一部分(键)是与自定义对应的URI
命名空间扩展和需要完全匹配目标命名空间属性,按照你自定义的XSD模式指定。
写作“META-INF/spring.schemas”
属性文件称为spring.schemas包含XML模式位置的映射
(与模式声明一同在使用该模式作为部分的XML文件中被引用
关于xsi:schemaLocation属性)映射到类路径资源。需要这个文件
以防止 Spring 必须使用默认实体解析器这需要
通过互联网访问以获取模式文件。如果你在这里指定映射
属性文件中,Spring 搜索该模式(此处为,myns.xsd在org.springframework.samples.xmlpackage)在类路径上。
以下摘录展示了我们需要为自定义模式添加的行:
http\://www.mycompany.example/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
(记住:必须逃脱角色。)
鼓励你将XSD文件(或文件)与之同时部署
这NamespaceHandler和BeanDefinition解析器课程在Classpath上。
在你的 Spring XML 配置中使用自定义扩展
使用你自己实现的自定义扩展和使用没有区别
这是Spring提供的“定制”扩展之一。如下
示例使用了 custom<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 | 我们的定制豆子。 |
更详细的例子
本节展示了一些更详细的自定义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/>元素是元件类别(见下一个示例)。注意元件类不会暴露
设置方法组件财产。这让它变得困难(或者说不可能)
为元件通过使用二传注射来上课。
以下列表显示了元件类:
-
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
}
}
解决这个问题的典型方法是创建一个自定义工厂豆这会暴露出一个
为组件财产。以下列表展示了这样的习俗工厂豆:
-
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())
}
}
接下来是习俗BeanDefinition解析器.记住我们在创造
一个豆子定义描述组件工厂豆.如下
列表显示我们的习惯BeanDefinition解析器实现:
-
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
“法线”元素上的自定义属性
写自己的自定义解析器和相关的产物并不难。然而 有时候这并不是正确的做法。考虑一个情景,你需要 为已有的豆子定义添加元数据。在这种情况下,你确实 我不想自己写整个自定义扩展。相反,你只是 想给现有的豆子定义元素添加一个额外的属性。
再举一个例子,假设你定义一个豆子定义 服务对象(它不知道)访问集群JCache,你要确保 命名为 JCache 实例的实例在周围集群中被迅速启动。 以下列表展示了此类定义:
<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"
jcache:cache-name="checking.account">
<!-- other dependencies here... -->
</bean>
然后我们可以创造另一个豆子定义当'jcache:cache-name'属性被解析。这豆子定义然后初始化
我们的名字叫JCache。我们也可以修改现有的豆子定义对于“查账服务”因此它对这个新的
JCache初始化豆子定义.以下列表展示了我们的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...
}
}
现在我们可以进入自定义扩展。首先,我们需要写作 描述自定义属性的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而不是BeanDefinition解析器.
以下列表展示了我们的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