領先一步
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...