Spring之context:property-placeholder詳解

概述

<context:property-placeholder>的做用是向Spring容器中注入一個屬性佔位解析器,用來處理BeanDefinition中的各類佔位符,用配置文件的信息替換佔位符。這是個自定義標籤,Spring會使用PropertyPlaceholderBeanDefinitionParser解析它。java

這個標籤能夠定義各類屬性數據結構

  • location:屬性文件路徑,符合Spring的Resource定義
  • properties-ref:指定Properties對象的bean
  • file-encoding
  • ignore-resource-not-found:默認false,表示屬性文件找不到就報錯
  • ignore-unresolvable:默認false,表示屬性找不到就報錯
  • local-override:默認false,若是localOverride=false,配置的優先級:environment > location > property-ref,若是localOverride=true,配置的優先級:environment < location < property-ref
  • system-properties-mode:ENVIRONMENT、NEVER、FALLBACK、OVERRIDE,默認是ENVIRONMENT
  • value-separator,間隔符,默認 ':'
  • trim-values,默認false,true表示解析佔位符的時候,執行trim操做去除兩端的空格
  • null-value,定義一個null值判斷,符合這個值的就表示應該返回null

PS:localOverride的運用,若是localOverride=false,配置的優先級:environment > location > property-ref,若是localOverride=true,配置的優先級:environment < location < property-ref
我從源碼進行分析,仔細看下這些屬性是怎麼運用的。app

<context:property-placeholder>的解析

這是個自定義標籤,Spring會使用PropertyPlaceholderBeanDefinitionParser解析它。ide

@Override
protected Class<?> getBeanClass(Element element) {
	// system-properties-mode=ENVIRONMENT
	if (element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIB).equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {
		return PropertySourcesPlaceholderConfigurer.class;
	}
	return PropertyPlaceholderConfigurer.class;
}

說明:post

  1. 若是system-properties-mode=ENVIRONMENT就註冊類型爲PropertySourcesPlaceholderConfigurer的Bean,不然爲PropertyPlaceholderConfigurer
  2. PropertySourcesPlaceholderConfigurer與PropertyPlaceholderConfigurer相比,就是增長environment中的屬性

PropertySourcesPlaceholderConfigurer

將全部的屬性,包括environment、location、properties-ref,組裝到PropertySources對象中。而後使用PropertySources去獲取須要替換屬性的真實值,依次迭代PropertySource,返回第一個找到的值。PropertySources是PropertySource的數據結構,裏面封裝多個PropertySource對象。ui

解析佔位符過程this

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    if (this.propertySources == null) {
        // 一、準備PropertySources屬性
        ...
    }

    // 二、解析佔位符
    processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
    this.appliedPropertySources = this.propertySources;
}

一、準備PropertySources屬性

if (this.propertySources == null) {
	this.propertySources = new MutablePropertySources();
	if (this.environment != null) {
		// 加入environment的PropertySource,名稱爲environmentProperties
		this.propertySources.addLast(
			new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
				@Override
				public String getProperty(String key) {
					// 從environment中獲取屬性
					return this.source.getProperty(key);
				}
			}
		);
	}
	try {
		// 加入配置文件的的PropertySource,名稱爲localProperties
		PropertySource<?> localPropertySource =
			new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());

		// 若是localOverride=true,就優先使用localProperties裏的屬性。作法是把localProperties的屬性放在environmentProperties的前面
		if (this.localOverride) {
			this.propertySources.addFirst(localPropertySource);
		}
		else {
			this.propertySources.addLast(localPropertySource);
		}
	}
	catch (IOException ex) {
		throw new BeanInitializationException("Could not load properties", ex);
	}
}

說明:spa

  1. 全部屬性放入PropertySources對象中
  2. environment的PropertySource,名稱爲environmentProperties
  3. 配置文件的的PropertySource,名稱爲localProperties

PS:localOverride的運用,若是localOverride=false,配置的優先級:environment > location > property-ref,若是localOverride=true,配置的優先級:environment < location < property-refdebug

1.一、mergeProperties()

protected Properties mergeProperties() throws IOException {
	Properties result = new Properties();

	if (this.localOverride) {
		// 先加載location定義的文件,會被property-ref中的屬性覆蓋
		// Load properties from file upfront, to let local properties override.
		loadProperties(result);
	}

	if (this.localProperties != null) {
		// 加載bean中定義的property對象
		for (Properties localProp : this.localProperties) {
			CollectionUtils.mergePropertiesIntoMap(localProp, result);
		}
	}

	if (!this.localOverride) {
		// 後加載location定義的文件,會覆蓋property-ref中的屬性
		// Load properties from file afterwards, to let those properties override.
		loadProperties(result);
	}

	return result;
}

二、解析佔位符

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			final ConfigurablePropertyResolver propertyResolver) throws BeansException {

	propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
	propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
	propertyResolver.setValueSeparator(this.valueSeparator);

	StringValueResolver valueResolver = strVal -> {
		String resolved = (ignoreUnresolvablePlaceholders ?
				propertyResolver.resolvePlaceholders(strVal) :
				propertyResolver.resolveRequiredPlaceholders(strVal));
		if (trimValues) {
			resolved = resolved.trim();
		}
		return (resolved.equals(nullValue) ? null : resolved);
	};

	doProcessProperties(beanFactoryToProcess, valueResolver);
}

說明:code

  1. ignoreUnresolvablePlaceholders=true,表示找不到不報錯。resolveRequiredPlaceholders方法找不到就拋錯
  2. trimValues=true,會使用trim去除空格
  3. nullValue表示的一個null值的表現
  4. 這個propertyResolver是上個方法建立的PropertySourcesPropertyResolver對象

三、PropertySourcesPropertyResolver

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
	if (this.propertySources != null) {
		for (PropertySource<?> propertySource : this.propertySources) {
			if (logger.isTraceEnabled()) {
				logger.trace("Searching for key '" + key + "' in PropertySource '" +
						propertySource.getName() + "'");
			}
			Object value = propertySource.getProperty(key);
			if (value != null) {
			// 先找到先返回
				if (resolveNestedPlaceholders && value instanceof String) {
					value = resolveNestedPlaceholders((String) value);
				}
				logKeyFound(key, propertySource, value);
				return convertValueIfNecessary(value, targetValueType);
			}
		}
	}
	if (logger.isDebugEnabled()) {
		logger.debug("Could not find key '" + key + "' in any property source");
	}
	return null;
}
相關文章
相關標籤/搜索