建立 Spring 2.0 命名空間?使用 Spring 的 AbstractBeanDefintionParser 階層架構。

工程 | Ben Hale | 2006 年 8 月 28 日 | ...

最近,我似乎一直專注於建立 Spring XML 命名空間。這一直是大量的試錯(在 XSD 和 Spring 方面)以獲得建立解析器的良好模式。我遇到的最大困惑之一是 AbstractBeanDefinitionParser 階層架構。目前,它沒有特別詳細的說明文件(但有一個 JIRA 對應到它,因此會在 GA 之前修復),所以我將向您介紹您的選擇、它們的用途以及如何使用它們。

AbstractBeanDefinitionParser 選擇

Spring 提供了三個主要的 BeanDefinitionParser 來幫助您解析 XML 命名空間。

我將從最特定的開始,逐步走向最一般的,以展示您在需要時如何獲得更多的能力。如果您想跳過範例並查看摘要,請查看這裡

AbstractSimpleBeanDefinitionParser

AbstractSimpleBeanDefinitionParserAbstractBeanDefinitionParser 中最特定的。這個類別旨在用於標籤上的屬性和 bean 上的屬性之間存在關聯的情況。 所以以下面的例子為例


<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 定義,並將其註冊到應用程式內容中。

AbstractSingleBeanDefinitionParser

AbstractSingleBeanDefinitionParser 稍微通用一些,我認為它將是最常用的抽象解析器。 這個類別讓您可以建立任何單個 bean 定義,該定義將自動註冊到內容中。 在這種情況下,bean 定義可能不是簡單的屬性映射,它可能具有複雜的巢狀結構,但它只會建立一個單個 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 定義解析的最常用的支持類別之一。

AbstractBeanDefinitionParser

現在是最可自訂的選擇,僅次於實際實現自己的介面。 基本上,這個特定的類別不僅允許您建立 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 上的屬性之間存在直接關聯,則使用 AbstractSimpleBeanDefinitionParser。 如果您要建立需要您執行一些 DOM 遍歷的單個 bean 定義,則使用 AbstractSingleBeanDefinitionParser。 如果前兩個太受限制並且您希望能夠任意註冊自己的 bean,則使用 AbstractBeanDefinitionParser。 最後,如果您真的喜歡自己做,您可以始終直接實現 BeanDefinitionParser 介面本身。

所以你有它了,一個快速的 bean 定義解析介紹。 我想知道的是你們有多少人正在做這件事? 您為哪些命名空間建立了命名空間,以及您如何使用解析器階層架構? 使用評論來發表您的意見。 誰知道呢,您的經驗和建議可能會作為增強功能進入 JIRA...


更新了最後一節中的錯字 更新了文字中 Defintion 的一致錯字

取得 Spring 電子報

透過 Spring 電子報保持聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進制檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

檢視全部