2018-10-16 · Develop

Java 配置文件-Spring

上篇文章讲解了使用自定义的 ConfigUtil 类读取 properties 的配置文件,这篇文章将讲解使用 spring 读取配置。

Spring 读取配置的方式还是比较多的,主要为如下几种方式:

1. <context:property-placeholder/>

<context:property-placeholder location="classpath:conf.properties" ignore-unresolvable="true"/>

上面的代码在3.1版之前是下面的简写:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
   <property name="ignoreUnresolvablePlaceholders" value="true"/>
   <property name="locations">
      <list>
         <value>classpath:conf.properties</value>
      </list>
    </property>
</bean>

而在3.1版之后是使用的 PropertySourcesPlaceholderConfigurer

<bean id="propertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
   <property name="ignoreUnresolvablePlaceholders" value="true"/>
   <property name="locations">
      <list>
         <value>classpath:conf.properties</value>
      </list>
    </property>
</bean>

在 JavaBean 中的使用方式

@Value("${username}")
private String username;

在xml 中使用:

<property name="username" value="${username}"/>

这里需要注意的几个地方:

获取的值不对

username 是系统变量,如果没有配置 <property name="localOverride" value="true"/> 的情况下:
PropertyPlaceholderConfigurer 会获取到我们配置中的值;而在 PropertySourcesPlaceholderConfigurer 中会获取到系统变量,即调用 System.getProperteis

获取不到值

不管 <context:property-placeholder/>PropertyPlaceholderConfigurer 还是 PropertySourcesPlaceholderConfigurer 这三种的那种方式,其本质是 BeanFactoryPostProcessor ,在处理 doProcessProperties 方法时调用如下代码:

	protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {

		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
		for (String curName : beanNames) {
			// Check that we're not parsing our own bean definition,
			// to avoid failing on unresolvable placeholders in properties file locations.
			if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
				BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
				try {
					visitor.visitBeanDefinition(bd);
				}
				catch (Exception ex) {
					throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
				}
			}
		}

		// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
		beanFactoryToProcess.resolveAliases(valueResolver);

		// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
		beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
	}

其中 addEmbeddedValueResolver 方法向一个LinkdList 中添加值,在取用的时候优先从链头取用,一旦发现无法找到值就抛出异常,如果系统中配置了多个 placeholder ,对外 Spring 就体现出其唯一性,但是 Spring 内部还是可以有多个 placeholder 的,只是在取到首位后面的被忽略掉了。

解决方案就是增加 :

<property name="order" value="1"/>

指定配置器执行顺序 order ,只让最后一个配置器解析不了占位符的时候抛出异常,其他的配置器在解析不了占位符时,交给后面的配置器去解析。

2. PropertiesFactoryBean

<bean id="properties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="locations">
        <array>
            <value>classpath:conf.properties</value>
        </array>
    </property>
</bean>

使用方式:

@Value("#{properties.username}")
// @Value("#{properties['username']}")
private String username;

或者:

<!-- <property name="username" value="#{properties['username']}"/> -->
<property name="username" value="#{properties.username}"/>

PropertiesFactoryBeanPropertiesLoaderSupport 直接的实现类, 专门用来管理 properties 文件的工厂 bean,默认是单例的。
PropertyPlaceholderConfigurer 是解决 properties 文件占位符问题的,也实现了 PropertiesLoaderSupport 类。
使用 #{properties.username}#{properties['username']} 的差别就不多讲了,相信你是知道的。

3. <util:properties/>

<util:properties id="properties" location="classpath:conf.properties"/>

使用方式同 PropertiesFactoryBean

4. PropertiesLoaderUtils

PropertiesLoaderUtils 是 Spring 提供的一个读取加载 properties 文件的工具类。

Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource("conf.properties"));

5. 其他方式

还有其他方式都是上面四种的变种,比如自定义类继承 PropertySourcesPlaceholderConfigurer ,在初始化 PropertySourcesPlaceholderConfigurer 时,将 properties 保持在自己的变量中,方便后续调用。


参考文档:
Spring PropertySourcesPlaceholderConfigurer工作原理
Spring配置文件context:property-placeholder标签使用漫谈
五种方式让你在java中读取properties文件内容不再是难题