Spring的PropertySourcesPlaceholderConfigurer的一点点小东东

时间:2022-10-21 05:13:08

最近的项目有这样一个需求:spring配置文件中的数据源配置需要加密,也就是读取的jdbc.properties文件中的url、username、password等参数需要进行DES加密,然后在spring把参数赋值给数据源之前解密。

先来看看Spring的一段配置:


<context:property-placeholder file-encoding="UTF-8" location="classpath:/config.properties,classpath:/jdbc.properties" ignore-resource-not-found="true" />

这里的配置就是spring在项目启动时将配置文件中的属性读取到了spring容器中。此处这段配置其实就是创建PropertySourcesPlaceholderConfigurer这个类来进行的管理。各位按ctrl点击标签可以看到spring-context.xsd中的详细信息。

PropertySourcesPlaceholderConfigurer类经过一系列继承关系,实质是一个容器后管理器。debug走里面的一段代码如下:
Spring的PropertySourcesPlaceholderConfigurer的一点点小东东
进入这个mergeProperties()方法里面看:
Spring的PropertySourcesPlaceholderConfigurer的一点点小东东
可以看到localOverride这个属性是用来控制是否覆盖Spring读取的属性配置。并且下面紧跟着判断this.localProperties!=null,如果localProperties不为null的话,会读取这些配置信息到spring容易中并且覆盖spring中已存在的属性。
知道这些后,我们在来看看如下配置:

<!-- 这里的local-override="true" 就是覆盖spring容器中已存在的属性,properties-ref="dataSourceProperties" 是指定自己的properties -->
<context:property-placeholder local-override="true" properties-ref="dataSourceProperties" file-encoding="UTF-8" location="classpath:/config.properties,classpath:/jdbc.properties" ignore-resource-not-found="true" />
<!-- 这个类是我自定义的,用来解密jdbc.properties中的属性之后然后存放到Properties类中 -->
<bean id="dataSourceProperties" class="com.sh.point.spring.propertiesHandle.DataSourceProperties">
<constructor-arg value="point.read.jdbc.username,point.read.jdbc.password,point.read.jdbc.url,point.write.jdbc.username,point.write.jdbc.password,point.write.jdbc.url"/>
</bean>

下面是我的DataSourceProperties类:

package com.sh.point.spring.propertiesHandle;

import com.sh.point.constant.Constant;
import com.sh.point.utils.DES;

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

/**
* 数据源配置参数处理
* <p/>
* 配置信息事先被DES加密处理,需要在此解密然后绑定到数据源
* Created by Alvin on 2016/7/31.
*/

public class DataSourceProperties extends Properties {

/**
* 构造方法
* @param propertyNames 需要解密的属性名称
*/

public DataSourceProperties(String[] propertyNames) {
try {
this.load(DataSourceProperties.class.getResourceAsStream("/jdbc.properties"));
for (String propertyName : propertyNames) {
decrypt(propertyName);
}
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 解密
*/

private void decrypt(String propertyName) {
String value = DES.decrypt(this.getProperty(propertyName), Constant.ENCRYPT_KEY);
this.setProperty(propertyName, value);
}

}

然后我的数据源配置如下:

<bean id="pointRead" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">

<description>mysql xa datasource</description>
<property name="uniqueResourceName">
<value>pointRead1</value>
</property>
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="user">${point.read.jdbc.username}</prop>
<prop key="password">${point.read.jdbc.password}</prop>
<prop key="URL">${point.read.jdbc.url}</prop>
</props>
</property>
<!-- 连接池里面连接的个数? -->
<property name="poolSize" value="3"/>
</bean>

jdbc.properties里面的属性加密过后如下:

point.read.jdbc.url=csRbLXGXGZ65TOILxkSPCOyJksX4F0kyPy/iKeGKC0g8F4eKZFmW7ynoYjceDsrfhTtF7TC/dM0YgXAYcd9DjB5OdIulTJvHkqD458IMRC0m7pmKQb26lLj3VF0jlh+gZegrg8Y/D7o=
point.read.jdbc.username=BbeUz7wzHcE=
point.read.jdbc.password=XcZSWnefvog=

point.write.jdbc.url=csRbLXGXGZ65TOILxkSPCOyJksX4F0kyPy/iKeGKC0g8F4eKZFmW7ynoYjceDsrfhTtF7TC/dM0YgXAYcd9DjB5OdIulTJvHkqD458IMRC0m7pmKQb26lLj3VF0jlh+gZegrg8Y/D7o=
point.write.jdbc.username=BbeUz7wzHcE=
point.write.jdbc.password=XcZSWnefvog=

这样就实现了上面的需求。properties文件中的属性加密,然后spring读取的时候先由上面的自己写的DataSourceProperties类来进行解密并且存储替换,然后spring就会把解密过后的属性存放在容器,并且根据spring配置文件中的EL表达式注入到数据源中。

搞定!