Spring基础:使用外部属性文件

来源:互联网 发布:光碟刻录器软件 编辑:程序博客网 时间:2024/06/15 12:32

前言

(本文通过阅读《精通Spring+4.x++企业应用开发实战》一书的总结)

在进行数据源或邮件服务器等资源的配置时,用户可以直接在Spring配置文件中配置用户名/密码、链接地址等信息。但一种更好地做法是将这些配置信息独立到一个外部属性文件中,并在Spring配置文件中通过形如${user}、${passwrod}的占位符引用属性文件中的属性项。这种配置方式拥有两个明显的好处。

  • 减少维护的工作量:资源的配置信息可以被多个应用共享,在多个应用使用同一资源的情况下,如果资源用户名/密码、链接地址等配置信息发生更改,则用户只需调整独立的属性文件即可。
  • 使部署更简单:Spring配置文件主要描述应用工程中的Bean,这些配置信息在开发完成后基本固定下来了。但数据源、邮件服务器等资源配置信息却需要在部署时根据现场情况确定。如果通过一个独立的属性文件存放这些配置信息,则部署人员只需调整这个属性文件即可,根本不需关注结构复杂、信息量大的Spring配置文件。这不仅给部署和维护带来了方便,也降低了出错的概率。

正题

Spring提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。PropertyPlaceholderConfigurer实现了BeanFactoryPOSTProcessorBean接口,因而也是一个Bean工厂后处理器。

PropertyPlaceholderConfigurer属性文件

在配置数据源是常用的一种配置如下:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"      destroy-method="close"      p:driverClassName="com.mysql.jdbc.Driver"      p:url="jdbc:mysql://localhost:3306/sampledb"      p:username="root"      p:password="root3306" />
驱动器类名、JDBC的URL地址及数据库用户名/密码都直接写在XML文件中,部署人员在部署应用时,必须先找出这个Bean配置XML文件,再找出数据源Bean定义的代码段进行调整,给部署工作带来了很大的困难。

根据实际应用中的最佳实战,可以将这些需要调整的配置信息抽取到一个配置文件中。这里使用一个名为jdbc.properties的配置文件,代码如下:

driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/sampledb"username="root"password="root3306"
属性文件可以定义多个属性,每个属性都由一个属性名和一个属性值组成,二者用“=”隔开。下面通过PropertyPlaceholderConfigurer引入jdbc.properties属性文件,调整数据源Bean的配置。

<!-- ①引入jdbc.properties属性文件 --><bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:location="classpath:jdbc.properties" p:fileEncoding="utf-8"/><!-- ②通过属性名引用属性值 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"      destroy-method="close"      p:driverClassName="${driverClassName}"      p:url="${url}"      p:username="${username}"      p:password="${passwordss}" />

在①处通过PropertyPlaceholderConfigurer的location属性引入属性文件,这样,在Bean定义的时候就可以引用属性文件中的属性了,如②处粗体部分所示。通过这样的调整后,部署人员在部署本应用时,仅需关心jdbc.properties这个配置文件即可,无须关心Spring的配置文件。

PropertyPlaceholderConfigurer的其他属性

除了location属性外,PropertyPlaceholderConfigurer还有一些常用的属性,在一些高级应用中可能会用到。

  • location:如果只有一个属性文件,则直接使用location属性指定就可以了;如果有多个属性文件,则可以通过locations属性进行设置。可以像配置List一样配置locations属性。
  • fileEncoding:属性文件的编码格式。Spring使用操作系统默认编码读取属性文件。如果属性文件采用了特殊编码,则需要通过该属性显示指定。
  • order:如果配置文件中定义了多个PropertyPlaceholderConfigurer,则通过该属性指定优先顺序。
  • placeholderPrefix:在上面的例子中,通过${属性名}引用属性文件中的属性项,其中“${”为默认的占位符前缀,可以根据需要改为其他的前缀符。
  • placeholderSuffix:占位符后缀,默认为“}”。

使用<context:property-placeholder>引用属性文件

可以使用context命名空间定义属性文件,相比传统的PropertyPlaceholderConfigurer配置,这种方法更加优雅。

<context:property-placeholder location="classpath:jdbc.properties"/>
以上配置就相当于在Spring容器中定义了一个PropertyPlaceholderConfigurer的Bean。虽然这种方式比较优雅,但如果希望自定义一些额外的高级功能,如属性加密、使用数据库表来保存配置信息等,则必须使用扩展PropertyPlaceholderConfigurer的类并使用Bean的配置方式。

在基于注解及基于Java类的配置中引用属性

在基于XML的配置文件中,通过“${popName}”形式引用属性值。类似的,基于注解配置的Bean可以通过@Value注解为Bean的成员变量或方法注入自动注入容器已有的属性,如下:

import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;@Componentpublic class MyDataSource {    @Value("${driverClassName}")    private String driverClassName;    @Value("${url}")    private String url;    @Value("${userName}")    private String userName;    @Value("${password}")    private String password;}
@Value注解可以为Bean注入一个字面值,也可以通过@Value("${propName}")的形式根据属性名注入属性值。由于标注@Configuration的类本身就相当标注了@Component,所以在标注@Configuration的类中引用属性的方式和基于注解配置的引用方式是完全一样的,此处不再赘述。

使用@Value注解来引用属性值带来很大的便利,但在使用过程中,一定要确保所引用的属性值在属性文件中已经存在且数值匹配,否则会造成Bean创建错误,引发意想不到的异常。

使用加密的属性文件

对于那些不敏感的属性信息,以明文形式出现在属性文件中是合适的。但如果属性信息是数据库用户名/密码等敏感信息,一般情况下则希望以密文的方式保存。虽然Web应用系统的客户端用户看不到服务器端的属性文件,但允许登录到Web应用系统所在服务器的内部人员却可以轻易查看到属性文件的内容。如果属性文件以明文形式保存着访问数据库的用户名/密码等信息,那么任何拥有服务器登录权限的人都可能查看到这些机密信息,容易造成数据库访问权限的泄露。

对于一些对安全要求特别高的应用系统(如电信、银行、公安的重点人口库等)来说,这些敏感信息应该只掌握在少数特定维护人员的手中,而不是毫无保留地对所有可以进入部署机器的开发人员开放。这就要求对应用程序配置文件的某些属性进行加密,让Spring容器在读取属性文件后,在内存中对属性文件进行解密,然后再将解密后的属性文件赋给目标对象。

PropertyPlaceHolderConfigurer继承自PropertyResourceConfigurer类,后者有几个有用的protected方法,用于在属性使用之前对属性列表中的属性进行转换。

  • void convertProperties(Properties props):属性文件的所有属性值都封装在props中,覆盖次方法,可以对所有的属性值进行转换处理。
  • String convertProperty(String propetryName,String propertyValue):在加载属性文件并读取文件中的每个属性时,都会调用此方法进行转换处理。
  • String convertPropertyValue(String originalValue):和上一个方法类似,只不过没有传入属性名。

在默认情况下,这3个方法内部都是空的,即不会对属性值进行任何转换。可以扩展PropertyPlaceholderConfigurer,覆盖相应的属性转换方法,就可以支持加密版的属性文件了。

PropertyPlaceholderConfigurer本身不支持密文版的属性文件,不过通过扩展该类,覆盖String convertProperty(String propertyName,String propertyValue)方法,对userName及password的属性值进行解密转换。

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {    private String[] encryptPropName = {"userName", "passowrd"};    @Override    protected String convertProperty(String propertyName, String propertyValue) {        if(isEncryptProp(propertyName)) {            //解密逻辑            //return decryptValue        } else {            return propertyValue;        }    }    private boolean isEncryptProp(String propertyName) {        for (String encryptPropName : encryptPropName) {            if(encryptPropName.endsWith(propertyName)) {                return  true;            }        }        return false;    }}
使用自定义的属性加载器后,就无法使用<context:property-placeholder>引用属性文件了,必须通过传统的配置方式引用加密版的属性文件
<bean class="com.smart.dynamic.EncryptPropertyPlaceholderConfigurer"    p:location="classpath:jdbc.properties"    p:fileEncoding="utf-8"/>
属性文件加载器的实现类改为EncryptPropertyPlaceholderConfigurer,其他配置和原来的相同。

原创粉丝点击