Spring PropertyPlaceholderConfigurer 自定义扩展

原文地址:https://blog.csdn.net/feiyu8607/article/details/8282893

Spring中PropertyPlaceholderConfigurer这个类,它是用来解析Java Properties属性文件值,并提供在spring配置期间替换使用属性值。接下来让我们逐渐的深入其配置。

 基本的使用方法是:

复制代码
<bean id="propertyConfigurerForAnalysis" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    <property name="location">  
        <value>classpath:/spring/include/dbQuery.properties</value>  
    </property>  
</bean> 
复制代码

其中classpath是引用src目录下的文件写法。

当存在多个Properties文件时,配置就需使用locations了:(2)

复制代码
<bean id="propertyConfigurer" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    <property name="locations">  
       <list>  
          <value>classpath:/spring/include/jdbc-parms.properties</value>  
          <value>classpath:/spring/include/base-config.properties</value>  
        </list>  
    </property>  
</bean>   
复制代码

接下来我们要使用多个PropertyPlaceholderConfigurer来分散配置,达到整合多工程下的多个分散的Properties文件,其配置如下:(3)

复制代码
<bean id="propertyConfigurerForProject1" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    <property name="order" value="1" />  
    <property name="ignoreUnresolvablePlaceholders" value="true" /> 
    <property name="locations">  
      <list>   
        <value>classpath:/spring/include/dbQuery.properties</value>     
      </list>
    </property>   
</bean>  
 
<bean id="propertyConfigurerForProject2" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">   
    <property name="order" value="2" />   
    <property name="ignoreUnresolvablePlaceholders" value="true" />   
    <property name="locations">   
      <list>   
        <value>classpath:/spring/include/jdbc-parms.properties</value>   
        <value>classpath:/spring/include/base-config.properties</value>   
      </list>   
    </property>   
</bean>
复制代码

其中order属性代表其加载的顺序,如果没有设置就按照加载xml文件时的顺序,而ignoreUnresolvablePlaceholders为是否忽略不可解析的Placeholder,如果配置了多个PropertyPlaceholderConfigurer,则该属性必须设置且为true,否则propertyConfigurerForProject2的properties文件不会被加载.

至此你已经了解到了如何使用PropertyPlaceholderConfigurer,如何使用多个Properties文件,以及如何配置多个PropertyPlaceholderConfigurer来分解工程中分散的Properties文件。至于PropertyPlaceholderConfigurer还有更多的扩展应用,如属性文件加密解密等方法将在之后的博文中续写。

注意事项:
(1)如果上面的dbQuery.properties与jdbc-parms.properties文件中有相同的参数配置名称,dbQuery.properties中配置的参数值不会被后面的覆盖;
(2)如果jdbc-parms.properties,base-config.properties彼此有相同参数名配置,jdbc-parms.properties中的配置的值会被覆盖;
 

自定义扩展PropertyPlaceholderConfigurer实现

例如:配置文件的路径,需要动态确定的,就需要自己扩展PropertyPlaceholderConfigurer的实现,自己获取文件路径,load properties文件,然后将load后的properties加入PropertyPlaceholderConfigurer

复制代码
package com.common.spring.ext;

import java.util.Properties;
import java.util.Set;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

import com.common.exception.ApplicationException;
import com.common.util.GlobalProperties;
import com.common.util.PropertiesUtil;


public class GollfPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    public void setGollfPropFiles(Set<String> gollfPropFiles) {
        String propPath = GlobalProperties.getProperty(GlobalProperties.PROPERTIES_FOLDER_PATH); //通过其他配置获取路径
        String fileSeparator = System.getProperty("file.separator");

        Properties properties = new Properties();
        for (String gollfPropFile : gollfPropFiles) {

            String nodeName = System.getProperty("weblogic.Name");
            gollfPropFile = gollfPropFile.replaceAll("\[NODE_NAME\]", nodeName); //NODE_NAME 是根据不同weblogic server确定

            String file = propPath + fileSeparator + gollfPropFile;

            try {
                logger.info("Loading properites file from " + file);
                Properties prop = PropertiesUtil.loadProperties(file); //返回properties文件
                logger.debug("Properties -> " + prop);
                if(prop != null) {
                    properties.putAll(prop);
                }
            } catch (Exception e) {
                logger.fatal(new ApplicationException("Properties file " + gollfPropFile + 
            " cannot be found. All related functionalities may be unavailable", e, true));
            }
        }

        this.setProperties(properties); //关键方法,调用的PropertyPlaceholderConfigurer中的方法,
                   //通过这个方法将自定义加载的properties文件加入spring中
    }

}
复制代码
复制代码
 <bean id="auditJmsProperties" 
       class="com.common.spring.ext.GollfPropertyPlaceholderConfigurer">
        <property name="gollfPropFiles">
            <set>
                <value>[NODE_NAME]_jms.properties</value>
            </set>
        </property>
    </bean>
复制代码

PropertyPlaceholderConfigurer中加载properties文件时,实际调用的:org.springframework.core.io.support.PropertiesLoaderSupport中的mergeProperties

Spring源码

复制代码
protected Properties mergeProperties() throws IOException {
  Properties result = new Properties();

  if (this.localOverride) {
   // Load properties from file upfront, to let local properties override.
   loadProperties(result);
  }

  if (this.localProperties != null) {
   for (Properties localProp : this.localProperties) {
       //将用户自定义加载的属性值,与spring加载的合并
       CollectionUtils.mergePropertiesIntoMap(localProp, result); 
     }
  }

  if (!this.localOverride) {
   // Load properties from file afterwards, to let those properties override.
   loadProperties(result);
  }

  return result;
 }
复制代码

将多个properties文件中的配置加载以后合并成一个Properties对象返回.
上面的this.setProperties(properties)方法,就是设置localProperties的引用,localProperties不为空的话,将用户自定义加载的properties属性合并到Spring加载的result Properties对象中
localOverride参数:为true的话,表示用户自定义加载的属性值覆盖spring系统加载的,如果同名的话.

自定义使用注意:用户自定义方法的调用务必在spring 初始化调用PropertyPlaceholderConfigurer的mergeProperties()方法之前调用,否则配置文件就没有合并.一般就set值的时候调用.

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

spring提供了一个PropertyPlaceholdConfigurer的 beanFactory后置处理器。可以将Bean配置的部分内容放到 属性文件 中,可以在Bean配置文件里使用形式为 ${var}的变量,PropertyPlaceholdConfigurer从属性文件里加载属性,并使用这些属性来替换变量。

以配置数据库连接字符串信息为例,配置如下:

复制代码
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties" />
</bean>
 
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="${initialSize}"></property>
    <!-- 连接池最大数量 -->
    <property name="maxActive" value="${maxActive}"></property>
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="${maxIdle}"></property>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="${minIdle}"></property>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="${maxWait}"></property>
</bean>
复制代码

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

使用spring的PropertyPlaceholderConfigurer加密properties文件中的属性

一、背景

处于安全考虑需要对.properties中的数据库用户名与密码等敏感数据进行加密。项目中使用了Spring3框架统一加载属性文件,所以最好可以干扰这个加载过程来实现对.properties文件中的部分属性进行加密。

属性文件中的属性最初始时敏感属性值可以为明文,程序第一次执行后自动加密明文为密文。

二、问题分析

  1. 扩展PropertyPlaceholderConfigurer最好的方式就是编写一个继承该类的子类。
  2. 外部设置locations时,记录全部locations信息,为加密文件保留属性文件列表。重写setLocations与setLocation方法(在父类中locations私有)
  3. 寻找一个读取属性文件属性的环节,检测敏感属性加密情况。对有已有加密特征的敏感属性进行解密。重写convertProperty方法来实现。
  4. 属性文件第一次加载完毕后,立即对属性文件中的明文信息进行加密。重写postProcessBeanFactory方式来实现。
三、代码实现
复制代码
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <!-- 加密 -->
        <property name="locations">
            <list>
                <value>classpath:db.properties</value>
            </list>
        </property>
        <!-- 声明需要加密的属性 -->
        <property name="encryptedProps">
            <set>
                <value>db.jdbc.username</value>
                <value>db.jdbc.password</value>
                <value>db.jdbc.url</value>
            </set>
        </property>
    </bean>
复制代码
复制代码
package com.sgfm.test;

import com.sgfm.base.des.DESEncryptUtil;

public class Test {
    private static final String SEC_KEY = "@^_^123aBcZ*"; // 主密钥
    private static final String ENCRYPTED_PREFIX = "{";// 默认加密前缀
    private static final String ENCRYPTED_SUFFIX = "}";// 默认加密后缀
    
    public static void main(String[] args) throws Exception {
        DESEncryptUtil dec = new DESEncryptUtil();
        String encryptStr = dec.encrypt("tcp://192.168.10.242:61616", SEC_KEY);// 加密
        String decryptStr = dec.decrypt("1B7328C79D9D5BC37E0636947EFEECD6E1459B4260497E31000754134655A350", SEC_KEY);// 解密
        
        System.out.println("加密内容:" + encryptStr);
        System.out.println("解密内容:" + decryptStr);
    }
}
复制代码
db.jdbc.driver=com.mysql.jdbc.Driver
db.jdbc.url=jdbc:mysql://localhost:3306/noah?useUnicode=true&characterEncoding=utf8
db.jdbc.username=noah
db.jdbc.password=noah
db.jdbc.driver=com.mysql.jdbc.Driver
db.jdbc.url=Encrypted:{e5ShuhQjzDZrkqoVdaO6XNQrTqCPIWv8i_VR4zaK28BrmWS_ocagv3weYNdr0WwI}
db.jdbc.username=Encrypted:{z5aneQi_h4mk4LEqhjZU-A}
db.jdbc.password=Encrypted:{v09a0SrOGbw-_DxZKieu5w}
原文地址:https://www.cnblogs.com/daxiong225/p/13943738.html