领先一步
VMware 提供培训和认证,助您加速进步。
了解更多最近我似乎一直专注于创建 Spring XML 命名空间。在 XSD 和 Spring 两方面,为了找到创建解析器的良好模式,经历了大量的尝试和错误。我遇到的最大困惑之一是 AbstractBeanDefinitionParser 层次结构。目前它的文档不是特别好(但有一个 JIRA 问题,所以会在 GA 之前修复),所以我将为您介绍您的选择、它们的作用以及如何使用它们。
我将从最具体的情况开始,然后逐步转向最概括的情况,以展示如何在需要时获得更多功能。如果您想跳过示例,直接查看摘要,请在此处查看。
<util:properties location="..." />
public class PropertiesFactoryBean extends PropertiesLoaderSupport
implements FactoryBean, InitializingBean {
...
public void setLocation(Resource location) {
this.locations = new Resource[] {location};
}
...
}
您会注意到 util:properties 标签上的 location 属性与 PropertiesFactoryBean 类型的 Java bean 属性相匹配。AbstractSimpleBeanDefinitionParser 会自动提取属性并将其映射到该属性。要实现此行为,您只需要实现一个名为 getBeanClass() 的方法。因此,此示例的实现如下:
public class PropertiesBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return PropertiesFactoryBean.class;
}
}
与所有抽象解析器一样,框架在后台为您处理 bean 定义的创建,并将其注册到应用程序上下文中。
<tx:advice>
<tx:attributes>
<tx:method name="get*" read-only="false" />
</tx:attributes>
</tx:advice>
public class TransactionInterceptor extends TransactionAspectSupport
implements MethodInterceptor, Serializable {
...
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
...
}
正如您所见,tx:advice 具有复杂的嵌套结构,这不像我们之前看到的那么一对一映射。然而,使用 AbstractSingleBeanDefinitionParser,您可以进行任意的 DOM 结构遍历,如下所示:
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
...
protected void doParse(Element element, BeanDefinitionBuilder builder) {
// Set the transaction manager property.
builder.addPropertyReference(TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY,
element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE));
List txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES);
if (txAttributes.size() > 1) {
throw new IllegalStateException("Element 'attributes' is allowed at most once inside element 'advice'");
}
else if (txAttributes.size() == 1) {
// Using attributes source.
parseAttributes((Element) txAttributes.get(0), builder);
}
else {
// Assume annotations source.
Class sourceClass = TxNamespaceUtils.getAnnotationTransactionAttributeSourceClass();
builder.addPropertyValue(TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, new RootBeanDefinition(sourceClass));
}
}
...
}
您可以看到,我们正在检查 DOM 并根据它做出复杂的 bean 定义决策。正如我之前所说的,我认为这将是用于 bean 定义解析的最常用的支持类之一。
<tx:annotation-driven />
熟悉 Spring 2.0 及其新命名空间的人应该会认识到这个标签,它是一个单行语句,可以自动检测 @Transactional 注解并代理包含它们的类。现在,在底层,创建了与 Spring 1.2.8 中 DefaultAutoProxyCreator 行为相同的 bean 定义集;总共 4 个 bean。那么这种行为的示例是什么样的呢?
class AnnotationDrivenBeanDefinitionParser extends AbstractBeanDefinitionParser {
...
protected BeanDefinition parseInternal(Element element, ParserContext parserContext) {
// Register the APC if needed.
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext);
boolean proxyTargetClass = TRUE.equals(element.getAttribute(PROXY_TARGET_CLASS));
if (proxyTargetClass) {
AopNamespaceUtils.forceAutoProxyCreatorToUseClassProxying(parserContext.getRegistry());
}
String transactionManagerName = element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE);
Class sourceClass = TxNamespaceUtils.getAnnotationTransactionAttributeSourceClass();
// Create the TransactionInterceptor definition
RootBeanDefinition interceptorDefinition = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDefinition.getPropertyValues().addPropertyValue(
TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, new RuntimeBeanReference(transactionManagerName));
interceptorDefinition.getPropertyValues().addPropertyValue(
TxNamespaceUtils.TRANSACTION_ATTRIBUTE_SOURCE, new RootBeanDefinition(sourceClass));
// Create the TransactionAttributeSourceAdvisor definition.
RootBeanDefinition advisorDefinition = new RootBeanDefinition(TransactionAttributeSourceAdvisor.class);
advisorDefinition.getPropertyValues().addPropertyValue(TRANSACTION_INTERCEPTOR, interceptorDefinition);
return advisorDefinition;
}
...
}
这里最大的新增功能是能够访问 ParserContext。此上下文使您能够将子元素委托给命名空间处理器,让它们的解析器创建并返回 bean 定义。这实际上是我非常喜欢的功能之一。ParserContext 还允许您创建自己的定义并直接注册它们,如果您愿意的话。
好了,这就是 bean 定义解析的快速入门。我想知道有多少人在这样做?您为哪些内容创建了命名空间,以及您是如何使用解析器层次结构的?在评论区发表您的看法。谁知道呢,您的经验和建议可能会转化为 JIRA 中的一个增强功能...