浅谈Spring的PropertyPlaceholderConfigurer

大型项目中,我们往往会对我们的系统的配置信息进行统一管理,一般做法是将配置信息配置与一个cfg.properties的文件中,然后在我们系统初始化的时候,系统自动读取cfg.properties配置文件中的key value(键值对),然后对我们系统进行定制的初始化。

  Spring中提供着一个PropertyPlaceholderConfigurer

  这个类是BeanFactoryPostProcessor的子类。(不懂自己度娘,可了解可不了解,下面我会讲大概)

其主要的原理在是。Spring容器初始化的时候,会读取xml或者annotation对Bean进行初始化。初始化的时候,这个PropertyPlaceholderConfigurer会拦截Bean的初始化,初始化的时候会对配置的${pname}进行替换,根据我们Properties中配置的进行替换。从而实现表达式的替换操作 。

PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是 BeanFactoryPostProcessor接口的一个实现。

PropertyPlaceholderConfigurer如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找。

(未验证真实性,摘自PropertyPlaceholderConfigurer读取配置文件 )

我们可以通过System.setProperty(key, value)或者java中通过-Dnamevalue来给Spring配置文件传递参数。

了解完原理之后,我们来看其实现的几种方式。

在我们的classpath下新建一个cfg.properties

#cfg.properties配置文件的内容
username=jay    
password=123

下面是Spring配置文件代码

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <!-- 对于读取一个配置文件采取的方案 -->
        <property name="location" value="classpath:cfg.properties"></property>
        <!-- 对于读取两个以上配置文件采取的处理方案 -->
        <!--
        <property name="locations"> 
            <list>
                <value>classpath:cfg.properties</value>
                <value>classpath:cfg2.properties</value>
            </list>
        </property>
        -->
    </bean>
</beans>
//测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationCtx.xml")
public class SpringCtxTst {


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

    @Value("${password}")
    private String pwd;

    @Test
    public void test(){
        System.out.println("username:"+uname);
        System.out.println("password:"+pwd);
    }
}

控制台输出

username:jay 
password:123

当然,Spring 为此也为我们提供了另外一个的解决方案,我们直接在Spring 配置文件中书写如下代码即可实现

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    <!--采用这种方式简化配置文件-->
    <context:property-placeholder location="classpath:cfg.properties,classpath:cfg2.properties"/>

</beans>
  • 注意

  我们知道,不论是使用PropertyPlaceholderConfigurer还是通过context:property-placeholder这种方式进行实现,都需要记住,Spring框架不仅仅会读取我们的配置文件中的键值对,而且还会读取Jvm 初始化的一下系统的信息。有时候,我们需要将配置Key定一套命名规则 ,例如

项目名称.组名.功能名=配置值
org.team.tfunction=0001

这种方式来尽量减少与系统配置信息的冲突! 
  同时,我们也可以使用下面这种配置方式进行配置,这里我配NEVER的意思是不读取系统配置信息。如果

<context:property-placeholder location="classpath:cfg.properties,classpath:cfg2.properties" system-properties-mode="NEVER"/>



继承PropertyPlaceholderConfigurer 实现动态加载配置文件

 在项目开发的过程中,往往有多个环境,dev,test,uat,run等.各个环境的配置文件是不同的.改造spring的PropertyPlaceholderConfigurer可以实现为各个不同的环境读取到各自的配置文件.这样为项目的部署省去很多工作. 

   Spring 的配置文件中,PropertyPlaceholderConfigurer 是占位符配置.为PropertyPlaceholderConfigurer 配置 .properties文件后,在Spring的配置文件中就可以使用${}的方式获取.properties数据. 
  假设现有dev,test,uat,run四个环境,在每个项目web容器启动参数里面添加不同启动参数,下图以dev为例 
这里写图片描述

  创建动态加载配置文件的DynamicServerConfig.java文件,核心方法为loadProperties,如下 
  

package com.iris.spring.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;

/**
 * 
 * @author xiami
 *
 */
public class DynamicServerConfig extends PropertyPlaceholderConfigurer {
    /** Logger available to subclasses */
    protected final Log logger = LogFactory.getLog(getClass());

    protected Properties[] localProperties;

    protected boolean localOverride = false;

    private Resource[] locations;

    private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();

    /**
     * copy
     */
    public void setLocations(Resource[] locations) {
        this.locations = locations;
    }

    /**
     * copy
     */
    public void setLocalOverride(boolean localOverride) {
        this.localOverride = localOverride;
    }

    /**
     * 根据启动参数,动态读取配置文件
     */
    protected void loadProperties(Properties props) throws IOException {
        if (locations != null) {
            logger.info("Start loading properties file.....");
            for (Resource location : this.locations) {
                InputStream is = null;
                try {
                    String filename = location.getFilename();
                    String env = "system." + System.getProperty("spring.profiles.active", "run") + ".properties";
                    if (filename.contains(env)) {
                        logger.info("Loading properties file from " + location);
                        is = location.getInputStream();
                        this.propertiesPersister.load(props, is);

                    }
                } catch (IOException ex) {
                    logger.info("读取配置文件失败.....");
                    ex.printStackTrace();
                    throw ex;
                } finally {
                    if (is != null) {
                        is.close();
                    }
                }
            }
        }

    }
}

  在Spring配置文件中applicationContext.xml中配置DynamicServerConfig.

<bean id="PropertyPlaceholderConfigurer" class="com.iris.spring.config.DynamicServerConfig">
        <property name="locations">
            <list>
                <value>classpath:config/system.development.properties</value>
                <value>classpath:config/system.run.properties</value>
                <value>classpath:config/system.test.properties</value>
                <value>classpath:config/system.uat.properties</value>
            </list>
        </property>
    </bean>

这样在tomcat启动的时候,Spring就会读取system.development.properties文件中的信息,不会读取其他几个文件中的信息.

上述的做法就是在applicationContext.xml中的DynamicServerConfig.的bean节点的list配置所有环境的properties文件名,

在jvm启动参数中配置当前想要连的环境的properties文件名(或文件名的unique部分),读取jvm启动参数,从而顺藤摸瓜读取相应环境的properties文件。

转载自:继承PropertyPlaceholderConfigurer 实现动态加载配置文件

怎么直接使用PropertyPlaceholderConfigurer 看下文

applicationContext.xml中的使用${}是代表什么意思?

一般情况下,如果你只有一个applicationContext.xml配置文件而已的话,那么只需要在applicationContext.xml文件中添加一行:

<!-- 导入外部的properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>  
其中:location属性是指该文件的位置。
如果是在src目录下的话,该位置为:classpath:文件名.后缀
如果是在/WEB-INF/目录下的话,该位置为: /WEB-INF/文件名.后缀。
但是要注意,不要放错位置了,<context:property-placeholder>不能发到<bean></bean>标签里面去。否则会报错;
 

PropertyPlaceholderConfigurer源码解析

前言

我们在开发中常遇到一种场景,Bean里面有一些参数是比较固定的,这种时候通常会采用配置的方式,将这些参数配置在.properties文件中,然后在Bean实例化的时候通过Spring将这些.properties文件中配置的参数使用占位符"${...}"替换的方式读入并设置到Bean的相应参数中。

这种做法最典型的就是JDBC的配置,本文就来研究一下.properties文件读取及占位符"${}"替换的源码,首先从代码入手,定义一个DataSource,模拟一下JDBC四个参数:

复制代码
 1 public class DataSource {
 2 
 3     /**
 4      * 驱动类
 5      */
 6     private String driveClass;
 7     
 8     /**
 9      * jdbc地址
10      */
11     private String url;
12     
13     /**
14      * 用户名
15      */
16     private String userName;
17     
18     /**
19      * 密码
20      */
21     private String password;
22 
23     public String getDriveClass() {
24         return driveClass;
25     }
26 
27     public void setDriveClass(String driveClass) {
28         this.driveClass = driveClass;
29     }
30 
31     public String getUrl() {
32         return url;
33     }
34 
35     public void setUrl(String url) {
36         this.url = url;
37     }
38 
39     public String getUserName() {
40         return userName;
41     }
42 
43     public void setUserName(String userName) {
44         this.userName = userName;
45     }
46 
47     public String getPassword() {
48         return password;
49     }
50 
51     public void setPassword(String password) {
52         this.password = password;
53     }
54 
55     @Override
56     public String toString() {
57         return "DataSource [driveClass=" + driveClass + ", url=" + url + ", userName=" + userName + ", password=" + password + "]";
58     }
59     
60 }
复制代码

定义一个db.properties文件:

 1 driveClass=0
 2 url=1
 3 userName=2
 4 password=3

定义一个properties.xml文件:

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans
 7         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8         http://www.springframework.org/schema/aop
 9         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
10 
11     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
12         <property name="location" value="properties/db.properties"></property>
13     </bean> 
14 
15     <bean id="dataSource" class="org.xrq.spring.action.properties.DataSource">
16         <property name="driveClass" value="${driveClass}" />
17         <property name="url" value="${url}" />
18         <property name="userName" value="${userName}" />
19         <property name="password" value="${password}" />
20     </bean>
21     
22 </beans>
复制代码

写一段测试代码:

复制代码
 1 public class TestProperties {
 2 
 3     @Test
 4     public void testProperties() {
 5         ApplicationContext ac = new ClassPathXmlApplicationContext("spring/properties.xml");
 6         
 7         DataSource dataSource = (DataSource)ac.getBean("dataSource");
 8         System.out.println(dataSource);
 9     }
10     
11 }
复制代码

运行结果就不贴了,很明显,下面就来分析一下Spring是如何将properties文件中的属性读入并替换"${}"占位符的。

 

PropertyPlaceholderConfigurer类解析

在properties.xml文件中我们看到了一个类PropertyPlaceholderConfigurer,顾名思义它就是一个属性占位符配置器,看一下这个类的继承关系图:

看到从这张图上,我们能分析出来的最重要的一点就是PropertyPlaceholderConfigurer是BeanFactoryPostProcessor接口的实现类,想见Spring上下文必然是在Bean定义全部加载完毕后且Bean实例化之前通过postProcessBeanFactory方法一次性地替换了占位符"${}"


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值