更上一层楼
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;
}
...
}
这里最大的补充是能够访问 <span style="font-family:courier>ParserContext。 此上下文使你能够将子元素委托给命名空间处理程序,并让他们的解析器创建和返回 bean 定义。 它实际上是我非常喜欢的功能之一。 <span style="font-family:courier>ParserContext 还允许你创建自己的定义并直接注册它们(如果你愿意)。
所以这就是它,bean 定义解析的快速介绍。 我想知道的是有多少人正在这样做? 你已经为哪些内容创建了命名空间,以及你如何使用解析器层次结构? 使用评论来表达你的声音。 谁知道呢,你的经验和建议可能会作为增强功能进入 JIRA...