Spring4.3.x 淺析xml配置的解析過程(6)——解析context命名空間之property-placeholder和property-override標籤

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/chyohn/article/details/54945777

概述


在上一篇解析自定義命名空間的標籤 中,我們已經知道解析自定義命名空間的標籤需要用到NamespaceHandler接口的實現類,並且知道spring是如何獲取命名空間對應的命名空間處理器對象的。因此我們很容易就能在spring-context包下的META-INF/spring.handlers文件中找到http://www.springframework.org/schema/context命名空間(即本文說的context命名空間)的處理器org.springframework.context.config.ContextNamespaceHandler,下面是ContextNamespaceHandler類的源碼。

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }

}

ContextNamespaceHandler 實現了NamespaceHandler接口的init方法來爲context命名空間下的標籤註冊解析器BeanDefinitionParser對象。

Spring context命名空間有property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export和mbean-server 8個標籤。這8個標籤都有一個BeanDefinitionParser實現類與之對應。這一節我們分別探討property-placeholder和property-override標籤的解析。

解析property-placeholder標籤


property-placeholder標籤用於加載property屬性文件。如果bean的<property>value值與屬性文件中的某個值相同,那麼,默認情況下,定義<property>的value值可以使用表達式“${key}”,其中key爲屬性文件中=左邊的字符串。

property-placeholder標籤對應的BeanDefinitionParser實現類是PropertyPlaceholderBeanDefinitionParser,下面是這個類的繼承結構。
這裏寫圖片描述

AbstractBeanDefinitionParser是PropertyPlaceholderBeanDefinitionParser類的一個抽象父類,它實現了BeanDefinitionParser接口的parse(Element element, ParserContext parserContext)方法,代碼如下。

    @Override
    public final BeanDefinition parse(Element element, ParserContext parserContext) {
        // 調用抽象方法parseInternal(Element element, ParserContext parserContext)
        // 這個方法有子類實現,把解析指定Element對象的任務交給子類完成
        AbstractBeanDefinition definition = parseInternal(element, parserContext);
        if (definition != null && !parserContext.isNested()) {
            try {
                // 解析並未bean生成一個id值
                String id = resolveId(element, definition, parserContext);
                if (!StringUtils.hasText(id)) {
                    parserContext.getReaderContext().error(
                            "Id is required for element '" + parserContext.getDelegate().getLocalName(element)
                                    + "' when used as a top-level tag", element);
                }
                String[] aliases = null;
                // 檢測是否應該把name屬性作爲bean的別名,默認爲true
                // 子類可以重寫shouldParseNameAsAliases()來決定
                if (shouldParseNameAsAliases()) {
                    // 獲取bean的別名
                    String name = element.getAttribute("name");
                    if (StringUtils.hasLength(name)) {
                        aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                    }
                }
                // 創建BeanDefinitionHolder對象
                BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
                // 註冊BeanDefintion
                registerBeanDefinition(holder, parserContext.getRegistry());
                if (shouldFireEvents()) {
                    BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
                    postProcessComponentDefinition(componentDefinition);
                    parserContext.registerComponent(componentDefinition);
                }
            } catch (BeanDefinitionStoreException ex) {
                parserContext.getReaderContext().error(ex.getMessage(), element);
                return null;
            }
        }
        return definition;
    }

AbstractBeanDefinitionParser的parse方法,首先把解析節點的任務交給子類來完成,子類需要實現parseInternal(Element element, ParserContext parserContext)方法並返回一個AbstractBeanDefinition 對象;然後根據需要設置bean的id和別名;最後創建並註冊BeanDefinitionHolder對象。下面我們重點看parseInternal方法,在PropertyPlaceholderBeanDefinitionParser的繼承體系中,parseInternal方法的實現在AbstractSingleBeanDefinitionParser類中,代碼如下。

    @Override
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        // 創建BeanDefinitionBuilder對象,這個對象只是代理了一個GenericBeanDefinition對象
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        String parentName = getParentName(element);
        if (parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }
        // 獲取要實例化bean的類對象,默認爲null,一般由子類提供。
        Class<?> beanClass = getBeanClass(element);
        if (beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        } else {
            String beanClassName = getBeanClassName(element);
            if (beanClassName != null) {
                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
            }
        }

        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        if (parserContext.isNested()) {
            // 嵌套的bean定義,必須和外層的bean在同一個作用域
            builder.setScope(parserContext.getContainingBeanDefinition().getScope());
        }
        if (parserContext.isDefaultLazyInit()) {
            // 默認爲延遲加載
            builder.setLazyInit(true);
        }
        // 把繼續解析標籤的任務交給子類
        doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }

AbstractSingleBeanDefinitionParser的parseInternal方法創建了一個BeanDefinitionBuilder對象,這個對象只是代理了GenericBeanDefinition對象,以簡化對GenericBeanDefinition對象的操作,然後就是一些基本的配置,這在代碼中已經體現了。parseInternal也並未對節點做實質性的操作,它只調用爲子類創建的一些鉤子方法,這些方法有:

用於獲取要實例化bean的類(Class)對象或者類全名稱的方法:

    protected Class<?> getBeanClass(Element element) {
        return null;
    }

    protected String getBeanClassName(Element element) {
        return null;
    }

以及用於進一步解析節點的方法

    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        doParse(element, builder);
    }

    protected void doParse(Element element, BeanDefinitionBuilder builder) {
    }

property-placeholder標籤的解析器PropertyPlaceholderBeanDefinitionParser類重寫了上面2組方法中的getBeanClass(Element element)方法和doParse(Element element, BeanDefinitionBuilder builder) 方法。
下面是getBeanClass(Element element)方法的源碼。

    @Override
    protected Class<?> getBeanClass(Element element) {

        // 從spring3.1開始system-properties-mode屬性的默認值就爲ENVIRONMENT,不再是FALLBACK
        if ("ENVIRONMENT".equals(element.getAttribute("system-properties-mode"))) {
            // PropertySourcesPlaceholderConfigurer對象可以從上下文的環境對象中獲取屬性值
            return PropertySourcesPlaceholderConfigurer.class;
        }

        return PropertyPlaceholderConfigurer.class;
    }

說明:PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer都是抽象類PlaceholderConfigurerSupport的直接子類,見總結。

下面是doParse(Element element, BeanDefinitionBuilder builder)方法的源碼。

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        super.doParse(element, builder);

        builder.addPropertyValue("ignoreUnresolvablePlaceholders",
                Boolean.valueOf(element.getAttribute("ignore-unresolvable")));

        String systemPropertiesModeName = element.getAttribute("system-properties-mode");
        if (StringUtils.hasLength(systemPropertiesModeName) &&
                !systemPropertiesModeName.equals("system-properties-mode")) {
            builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName);
        }

        // 指定一個分隔符用於分隔默認值,默認爲英文冒號:
        // 比如${user.name:chyohn},如果user.name沒有在屬性文件中定義,則使用默認值chyohn
        // 假設設置的分隔符爲英文?,則上面的定義應該爲${name?choyhn}
        if (element.hasAttribute("value-separator")) {
            builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator"));
        }
        // 設置是否允許trim獲取到的屬性值,默認爲false
        if (element.hasAttribute("trim-values")) {
            builder.addPropertyValue("trimValues", element.getAttribute("trim-values"));
        }
        // 指定一個值用於表示null,比如指定的爲hasNull,
        // 如果某一個屬性值定義的時候爲haveNull,那麼生成的bean的這個屬性真正值就是null
        if (element.hasAttribute("null-value")) {
            builder.addPropertyValue("nullValue", element.getAttribute("null-value"));
        }
    }

PropertyPlaceholderBeanDefinitionParser類的doParse方法首先調用父類AbstractPropertyLoadingBeanDefinitionParser的doParse方法,然後根據節點配置的屬性值來修改PlaceholderConfigurerSupport的屬性值。這裏我們在繼續看看AbstractPropertyLoadingBeanDefinitionParser的doParse方法源代碼。

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        // 獲取屬性文件的地址參數
        String location = element.getAttribute("location");
        if (StringUtils.hasLength(location)) {
            // 如果有多個屬性文件,每個屬性文件可以使用英文逗號隔開。
            String[] locations = StringUtils.commaDelimitedListToStringArray(location);
            builder.addPropertyValue("locations", locations);
        }

        // 獲取指定的Properties對象的bean名稱
        // 如果local-override屬性爲true,這裏設置的properties將覆蓋屬性文件中的內容
        String propertiesRef = element.getAttribute("properties-ref");
        if (StringUtils.hasLength(propertiesRef)) {
            builder.addPropertyReference("properties", propertiesRef);
        }

        // 獲取屬性文件編碼
        String fileEncoding = element.getAttribute("file-encoding");
        if (StringUtils.hasLength(fileEncoding)) {
            builder.addPropertyValue("fileEncoding", fileEncoding);
        }

        // 設置排序,即優先級
        String order = element.getAttribute("order");
        if (StringUtils.hasLength(order)) {
            builder.addPropertyValue("order", Integer.valueOf(order));
        }

        // 設置是否忽略指定的屬性文件不存在的錯誤,true爲是
        builder.addPropertyValue("ignoreResourceNotFound",
                Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));

        // 一般來說,屬性文件的內容更後加載。
        // 如果localOverride爲true,那麼PropertiesLoaderSupport的localProperties內容就會覆蓋屬性文件中相同key的的內容。
        // 如果標籤是property-placeholder且localOverride爲true,上下文的環境對象中的數據也會覆蓋屬性文件中相同key的內容。
        // 默認爲false
        builder.addPropertyValue("localOverride",
                Boolean.valueOf(element.getAttribute("local-override")));

        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    }

解析property-override標籤


property-override標籤的作用是爲xml配置文件中的bean的屬性指定最終結果。

property-override標籤的解析器類爲PropertyOverrideBeanDefinitionParser,這個類和property-placeholder標籤的解析器類PropertyPlaceholderBeanDefinitionParser一樣是AbstractPropertyLoadingBeanDefinitionParser類的直接子類,並且PropertyOverrideBeanDefinitionParser重寫了getBeanClass(Element element)和doParse(Element element, BeanDefinitionBuilder builder)方法,代碼如下。

    @Override
    protected Class<?> getBeanClass(Element element) {
        return PropertyOverrideConfigurer.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        super.doParse(element, builder);
        // 設置忽略不正確的鍵,也就是忽略格式不正確的鍵,或者是.左邊的bean名稱不存在的鍵
        // 默認爲false,表示不忽略
        builder.addPropertyValue("ignoreInvalidKeys",
                Boolean.valueOf(element.getAttribute("ignore-unresolvable")));

    }

關於AbstractPropertyLoadingBeanDefinitionParser類在前面已經探討過了,關於這段代碼也就沒有什麼要多說的。

總結


(1)property-override和property-placeholder的不同點。

  • 功能不一樣,property-override標籤的作用是爲xml配置文件中的bean的屬性指定最終結果;而property-placeholder標籤的作用是把xml配置文件中bean 的<property>標籤的value值替換成正真的值,而且<property>標籤的value值必須符合特定的表達式格式,默認爲“${key}”,其中key爲屬性文件中的key。

  • 屬性文件內容要求不一樣,property-override標籤加載的properties文件中的key的格式有嚴格的要求,必須爲“bean名稱.bean屬性”。如果屬性ignore-unresolvable的值爲false,那麼屬性文件中的bean名稱必須在當前容器中能找到對應的bean。

(2)property-override和property-placeholder的共同點。

  • 兩者都是以properties文件作爲數據來源。

  • 兩者的解析器BeanDefinitionParser類都繼承自AbstractPropertyLoadingBeanDefinitionParser類。因此它們共有AbstractPropertyLoadingBeanDefinitionParser及其父類中所處理的標籤屬性,並且這些屬性在兩個標籤中具有相同的作用。這其實都歸於它們所代表的工廠後處理器都繼承了PropertiesLoaderSupport類,具體看下面的繼承結構圖,其中第一個是property-override的,後面兩個是property-placeholder的。
    PropertyOverrideConfigurer
    PropertyPlaceholderConfigurer
    PropertySourcesPlaceholderConfigurer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章