spring和spring
在本文中,我将描述Spring的一个有用但未被充分利用的功能,即Spring bean定义文件中的自定义标签的定义。
Spring名称空间
我将从以Spring文档为例的简单示例开始。 在2.0版之前,只有一个XML模式可用。 因此,为了使一个常量可以作为bean使用,并将其注入其他bean,必须定义以下内容:
<beanid="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
Spring使它成为可能,但是意识到将常量公开为bean只是一个技巧。 但是,从Spring 2.0开始,该框架允许您使用util
名称空间,从而使前面的示例变为:
<util:constantstatic-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
实际上,现在有许多名称空间可用:
字首 | 命名空间 | 描述 |
| | Original bean schema |
| | Utilities: constants, property paths and collections |
| | JNDI lookup |
| | Use of other languages |
| | Transactions |
| | AOP |
| | |
如第一个示例所示,每个选项都旨在减少冗长程度并提高可读性。
创作
许多人仍然不知道该功能是可扩展的,Spring API为您提供了编写自己的功能的途径。 实际上,许多框架提供者都应该利用这一点并提供自己的名称空间,以便更轻松地将其产品与Spring集成。 有些已经做到了:想到了带有许多名称空间的 CXF ,但应该有其他我不认识的名称空间 。
创建您自己的名称空间的过程分为4个步骤:关于XML验证的2个步骤,另外两个用于创建bean本身。 为了说明该过程,我将使用一个简单的示例:我将为EhCache (Hibernate的默认缓存引擎)创建一个架构。
基础bean工厂将是现有的EhCacheFactoryBean 。 因此,它不会像真正的功能那样有用,但是它将使我们专注于真正的编写管道,而不是EhCache实现细节。
创建模式
创建模式是关于描述XML语法,更重要的是限制。 我希望我的XML如下所示:
<ehcache:cacheid="myCache"eternal="true"cacheName="foo"
maxElementsInMemory="5"maxElementsOnDisk="2"overflowToDisk="false"
diskExpiryThreadIntervalSeconds="18"diskPersistent="true"timeToIdle="25"timeToLive="50"
memoryStoreEvictionPolicy="FIFO">
<ehcache:managerref="someManagerRef"/>
</ehcache:echcache>
因为我不会假定要教任何人有关XML的知识,所以这里是模式。 只要注意名称空间声明即可:
<xsd:schemaxmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://blog.frankel.ch/spring/ehcache"xmlns="http://blog.frankel.ch/spring/ehcache"
elementFormDefault="qualified">
<xsd:complexTypename="cacheType">
<xsd:sequencemaxOccurs="1"minOccurs="0">
<xsd:elementname="manager"type="managerType"/>
</xsd:sequence>
<xsd:attributename="id"type="xsd:string"/>
<xsd:attributename="cacheName"type="xsd:string"/>
<xsd:attributename="diskExpiryThreadIntervalSeconds"type="xsd:int"/>
<xsd:attributename="diskPersistent"type="xsd:boolean"/>
<xsd:attributename="eternal"type="xsd:boolean"/>
<xsd:attributename="maxElementsInMemory"type="xsd:int"/>
<xsd:attributename="maxElementsOnDisk"type="xsd:int"/>
<xsd:attributename="overflowToDisk"type="xsd:boolean"/>
<xsd:attributename="timeToLive"type="xsd:int"/>
<xsd:attributename="timeToIdle"type="xsd:int"/>
<xsd:attributename="memoryStoreEvictionPolicy"type="memoryStoreEvictionPolicyType"/>
</xsd:complexType>
<xsd:simpleTypename="memoryStoreEvictionPolicyType">
<xsd:restrictionbase="xsd:string">
<xsd:enumerationvalue="LRU"/>
<xsd:enumerationvalue="LFU"/>
<xsd:enumerationvalue="FIFO"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexTypename="managerType">
<xsd:attributename="ref"type="xsd:string"/>
</xsd:complexType>
<xsd:elementname="cache"type="cacheType"/>
</xsd:schema>
对于像我一样喜欢图形显示的人:
映射架构
模式创建只是第一部分。 现在,我们必须让Spring意识到这一点。 创建文件META-INF / spring.schemas并在下面的行中写就足够了:
http\://blog.frankel.ch/spring/schema/custom.xsd=ch/frankel/blog/spring/authoring/custom.xsd
请小心插入反斜杠,否则它将不起作用。 它将XML中的模式声明映射到将在jar中使用的真实文件!
在更进一步和好奇之前,请注意在spring-beans.jar(v3.0)中有一个这样的文件。 内容如下:
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
它带来一些注释:
- 春天吃自己的狗粮(很高兴知道)
- 我没有研究代码,但是我认为这就是为什么对Spring Bean文件的XML验证永远不会抱怨无法通过Internet找到架构(由于防火墙安全性问题,在生产环境中确实很痛苦)。 那是因为XSD是在罐子里看的
- 如果您未指定要使用的Spring模式的版本(2.0、2.5、3.0等),则Spring将使用jar的每个主要/次要版本自动为您升级它。 如果您想要这种行为,很好,否则,您必须指定版本
创建解析器
前面的步骤仅用于验证XML,以使eternal属性采用布尔值。 我们仍然没有将名称空间连接到Spring工厂。 这是此步骤的目标。
首先要做的是创建一个实现org.springframework.beans.factory.xml.BeanDefinitionParser的类。 从其层次结构来看,似乎org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser是一个很好的入口点,因为:
- XML不太复杂
- 将只有一个bean定义
这是代码:
publicclassEhCacheBeanDefinitionParserextendsAbstractSimpleBeanDefinitionParser{
privatestaticfinalList<String>PROP_TAG_NAMES;
static{
PROP_TAG_NAMES=newArrayList();
PROP_TAG_NAMES.add("eternal");
PROP_TAG_NAMES.add("cacheName");
PROP_TAG_NAMES.add("maxElementsInMemory");
PROP_TAG_NAMES.add("maxElementsOnDisk");
PROP_TAG_NAMES.add("overflowToDisk");
PROP_TAG_NAMES.add("diskExpiryThreadIntervalSeconds");
PROP_TAG_NAMES.add("diskPersistent");
PROP_TAG_NAMES.add("timeToLive");
PROP_TAG_NAMES.add("timeToIdle");
}
@Override
protectedClassgetBeanClass(Elementelement){
returnEhCacheFactoryBean.class;
}
@Override
protectedbooleanshouldGenerateIdAsFallback(){
returntrue;
}
@Override
protectedvoiddoParse(Elementelement,ParserContextparserContext,BeanDefinitionBuilderbuilder){
for(Stringname:PROP_TAG_NAMES){
Stringvalue=element.getAttribute(name);
if(StringUtils.hasText(value)){
builder.addPropertyValue(name,value);
}
}
NodeListnodes=element.getElementsByTagNameNS("http://blog.frankel.ch/spring/ehcache","manager");
if(nodes.getLength()>0){
builder.addPropertyReference("cacheManager",
nodes.item(0).getAttributes().getNamedItem("ref").getNodeValue());
}
Stringmsep=element.getAttribute("memoryStoreEvictionPolicy");
if(StringUtils.hasText(msep)){
MemoryStoreEvictionPolicypolicy=MemoryStoreEvictionPolicy.fromString(msep);
builder.addPropertyValue("memoryStoreEvictionPolicy",policy);
}
}
}
这值得一些解释。 静态块填充哪些属性有效。 getBeanClass()方法返回将使用的类,直接用作Bean或用作工厂。 shouldGenerateIdAsFallback()方法用于告诉Spring,如果XML中没有提供ID,则应生成一个ID。 这样就可以创建伪匿名Bean(在Spring工厂中,没有一个Bean真正是匿名的)。
真正的魔力发生在doParse()
方法中:它只是添加在构建器中找到的每个简单属性。 但是,有两个有趣的属性: cacheManager
和memoryStoreEvictionPolicy
。
前者(如果存在)是对另一个bean的引用。 因此,不应将其作为参考值添加到构建器中。 当然,代码不会检查开发人员是否在ehcache内将缓存管理器声明为匿名Bean,但是模式验证已经解决了这一问题。
后者仅使用字符串值来获取真实对象,并将其作为属性添加到构建器。 同样,由于该值是在架构上枚举的,因此不会发生由语法错误引起的异常。
注册解析器
最后一步是在Spring中注册解析器。 首先,您只需要创建一个扩展org.springframework.beans.factory.xml.NamespaceHandlerSupport的类,并在其init()
方法中以XML标记名称注册处理程序:
publicclassEhCacheNamespaceHandlerextendsNamespaceHandlerSupport{
publicvoidinit(){
registerBeanDefinitionParser("cache",newEhCacheBeanDefinitionParser());
}
}
如果您有更多解析器,只需在每个标签名称下以相同的方法注册它们即可。
其次,只需将先前创建的名称空间映射到文件META-INF / spring.handlers中的新创建的处理程序:
http\://blog.frankel.ch/spring/ehcache=ch.frankel.blog.spring.authoring.ehcache.EhCacheNamespaceHandler
请注意,您将声明的架构文件映射到实际架构,但将名称空间映射到处理程序。
结论
现在,当面对过于冗长的bean配置时,您可以选择使用这种漂亮的4步技术来简化它。 当然,该技术更面向产品提供者,但是可以由项目使用,前提是编写名称空间所花的时间比常规bean定义要实时得多。
您可以在此处以Maven / Eclipse格式找到本文的资源。
更进一步:
- Spring创作
- Spring的Javadoc与创作有关