领先一步
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……