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;
}
相关文章
相关标签/搜索