建立 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 社群中所有即將舉行的活動。

檢視全部