在进行数据源或邮件服务器等资源的配置时,用户可以直接在Spring的配置文件中配置用户名和密码及连接地址等信息。但更好的一种做法是将这些配置信息独立到一个外部属性文件中,并在Spring配置文件中通过形如 $ {user}、$ {password}等占位符引用属性文件中的属性项,这种配置的方式拥有两个显而易见的好处:
首先是可以减少维护的工作量,资源的配置信息可以被多个应用共享,在多个应用使用同一资源的情况下,如果资源用户名/密码及链接地址等配置信息发生更改时,用户只要调整独立的属性文件就可以了。
其次可以使得部署更加简单:Spring配置文件主要描述应用工程中的Bean,这些配置信息在开发完成后就基本固定下来了。但是数据源、邮箱服务器等资源配置信息却需要在部署时根据需要设定。如果通过一个独立的属性文件存放这些配置信息,应用部署人员只需要调整这个属性文件即可,而不需要取关注结构复杂信息量大的Spring配置文件。不仅给部署和维护带来了方便,也降低了出错的机率。
Spring为我们提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessorBean接口,因此也是一个Bean工厂后处理器。
1.使用外部属性文件
使用属性文件
首先我们来看一下定义的数据源:
<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=:1234:/>
驱动器类名,JDBC的URL地址及数据库的用户名密码都直接写在XML文件中,部署人员在部署应用时,必须找到这个Bean定义所在的XML文件,找出数据源Bean定义的代码段进行调整,给部署工作带来很大困难。
根据实际应用中的最佳实战,可以将这些需要调整的配置信息抽取到一个配置文件里。这里,我们使用一个名为jdbc.properties的属性文件:
dirverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306.sampledb
userName=root
password=1234
属性文件可以定义多个属性,每个属性都由一个属性名和一个属性值组成,两者用=相连,下面通过PropertyPlaceholderConfigurer引入jdbc.properties属性文件,调整数据源Bean的配置:
<!--引入jdbc.properties的属性文件-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:com/baobaotao/placeholder/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="${password}"/>
我们使用PropertyPlaceholderConfigurer的location引入了属性文件,这样,在Bean定义的时候就可以引用属性文件中的属性了,通过这样的调整后,部署人员在部署本应用时,仅需关注jdbc.properties这个配置文件就可以了,无须关心Spring的配置文件。
PropertyPlaceholderConfigurer其他属性
除了location属性外,PropertyPlaceholderConfigurer还有一些常见的属性,在一些高级应用中,我们可能会用到。
1.locations:如果只有一个属性文件,直接使用location属性指定就可以了,如果是多个属性文件,则可以通过locations属性进行设置。
2.fileEncoding:属性文件的编码格式。
3.order:如果配置文件中定义了多个PropertyPlaceholderConfigurer,则通过该属性指定优先顺序。
4.placeholderPrefix:在上面的例子中,我们通过$ {属性名}引用属性文件中的属性项,其中"$“是默认的占位符前缀,可以根据需要改为其他的前缀符。
5.placeholderSuffix:占位符后缀,默认为”}"。
使用< context:property-place-placeholder >引用属性文件
可以使用context命名空间定义属性文件,相比于传统的PropertyPlaceholderConfigurer配置,这种方法更加优雅:
<context:property-placeholder
location="classpath:com/baobaotao/placeholder/jdbc.properties"/>
以上配置就相当于在Spring容器中定义了一个PropertyPlaceholderConfigurer的Bean,不过,有一点值得注意的是,虽然该元素支持file-encoding属性指定属性文件的编码方式,但是直接像file-encoding="utf-8"这样设置时,会报异常,必须将"utf-8"声明为一个字符串Bean才不会报错:
<context:property-placeholder
location="classpath:com/baobaotao/placeholder/jdbc.properties"
file-encoding="utf8"/>
<bean id="utf8" class="java.lang.String">
<constructor-arg value="utf-8"/>
</bean>
>
这个算是Spring的一个BUG,期待在后面版本可以让file-encoding属性接收字面值的配置。
在基于注解及基于Java类配置中引入属性
在基于XML的配置文件中,我们通过"${propName}"的形式引用属性值。类似的,基于注解配置的Bean可以通过@Value的注解,为Bean的成员变量或方法入参自动注入容器已有的属性,如下所示:
package com.baobaotao.placeholder;
package org.apache.commons.lang.builder.ToStringBuilder;
import org,springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyDataSource{
@Value("${driverClassName}")
private String driverClassName;
@Value("${url}")
private String url;
@Value("${UserName}")
private String userName;
@Value("${password}")
private String password;
public String toString(){
return ToStringBuilder.reflectionToString(this);
}
}
@Value注解可以为Bean注入一个字面值,也可以通过@Value("${propName}")的形式根据属性名注入属性值。由于标注了@Configuration的类本身相当于标注了@Component,所以标注@Configuration类中引用属性的方法与上述方法一致。在这里不多赘述。
2.属性文件自身的引用
Spring既允许在Bean定义中通过${propName}引用属性值,还允许在属性文件中使用 ${propName},实现属性之间的相互引用。
dbName=sampledb
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/${dbName}
在上面的属性文件定义中,url属性的值通过${dbName}引用了另一个属性的值。对于一些复杂的属性,我们可以通过这种方式将属性变化的部分抽取出来,实现配置的最小化。
注:如果一个属性值太长,一行写不下,可以通过在行后添加""将属性值划分为多行,比如:
desc=desc.content desc content desc content\
desc content desc content