一、PropertyPlaceholderConfigurer作用
要了解这个类首先要弄清楚一个概念:bean factory post-processor
官方解释是这样的:
A bean factory post-processor is a java class which implements the
org.springframework.beans.factory.config.BeanFactoryPostProcessor interface. It is executed manually (in the case of the BeanFactory) or automatically (in the case of the ApplicationContext) to apply changes of some sort to an entire BeanFactory, after it has been constructed.
我理解的意思是这样的:
1.首先bean factory post-processor实现了org.springframework.beans.factory.config.BeanFactoryPostProcessor接口。
2.在BeanFactory的情况下它被手动的执行。
3.在ApplicationContext的条件下它会自动的执行。
4.最关键的一点是,是在一个类的实例被构造出来之后,对整个BeanFactory进行修改。
那么PropertyPlaceholderConfigurer类就是bean factory post-processor的一种,它的作用是一个资源属性的配置器,能够将BeanFactory的里定义的内容放在一个以.propertis后缀的文件中。PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是 BeanFactoryPostProcessor接口的一个实现。PropertyPlaceholderConfigurer可以将上下文(配置文 件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值。这样的话,只需要对properties文件进 行修改,而不用对xml配置文件进行修改。
二、PropertyPlaceholderConfigurer配置
例如使用如下:
<bean id="dataSource"
class="org.logicalcobwebs.proxool.ProxoolDataSource"
destroy-method="close">
<property name="driver">
<value>${jdbc.driver}</value>
</property>
<property name="driverUrl">
<value>${jdbc.driverUrl}</value>
</property>
<property name="user">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<property name="alias">
<value>${proxool.alias}</value>
</property>
<property name="prototypeCount">
<value>${proxool.prototypeCount}</value>
</property>
<property name="maximumConnectionCount">
<value>${proxool.maximumConnectionCount}</value>
</property>
<property name="minimumConnectionCount">
<value>${proxool.minimumConnectionCount}</value>
</property>
<property name="simultaneousBuildThrottle">
<value>${proxool.simultaneousBuildThrottle}</value>
</property>
<property name="trace">
<value>${proxool.trace}</value>
</property>
<property name="verbose">
<value>${proxool.verbose}</value>
</property>
<property name="houseKeepingTestSql">
<value>${proxool.houseKeepingTestSql}</value>
</property>
</bean>
而实际的jdbc.propertis文件是
#jdbc settings
jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl10g
jdbc.username=user
jdbc.password=name
jdbc.driver=oracle.jdbc.driver.OracleDriver
#hibernate settings
hibernate.show_sql=false
#hibernate.format_sql=false
#hibernate.ehcache_config_file=/ehcache/ehcache-hibernate-local.xml
#pool parameters
initialPoolSize=2
maxPoolSize=10
#maxIdleTime=10
#idleConnectionTestPeriod=5
autoCommitOnClose=true
而jdbc.propertis是怎样引用的呢:
---applicationContext.xml----
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName"
value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath*:/jdbc.properties</value>
</list>
</property>
</bean>
将上边一段配置注册在web.xml中就可以了
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:configs/**/applicationContext-*.xml,classpath*:com/**/applicationContext-*.xml
</param-value>
</context-param>
当然,不要忘了spring的监听器注册
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
这样,一个简单的数据源就设置完毕了。实际上,PropertyPlaceholderConfigurer起的作用就是将占位符指向的数据库配置信息放在bean中定义的工具。
三、PropertyPlaceholderConfigurer的高级属性
2.在Spring中,使用PropertyPlaceholderConfigurer可以在XML配置文件中加入外部属性文件,当然也可以指定外部文件的编码,如:
<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>conf/sqlmap/jdbc.properties</value>
</property>
<property name="fileEncoding">
<value>UTF-8</value>
</property>
</bean>
当然也可以引入多个属性文件,如:
<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>/WEB-INF/mail.properties</value>
<value>classpath: conf/sqlmap/jdbc.properties</value>//注意这两种value值的写法
</list>
</property>
</bean>
3.譬如,jdbc.properties的内容为:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/mysqldb?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=round;
jdbc.username=root
jdbc.password=123456
4.那么在spring配置文件中,我们就可以这样写:
<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath: conf/sqlmap/jdbc.properties </value>
</list>
</property>
</bean>
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<property name="driverClassName"value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}"/>
<property name="password"value="${jdbc.password}" />
</bean>
5.这样,一个简单的数据源就设置完毕了。可以看出:PropertyPlaceholderConfigurer起的作用就是将占位符指向的数据库配置信息放在bean中定义的工具。
6.查看源代码,可以发现,locations属性定义在PropertyPlaceholderConfigurer的祖父类 PropertiesLoaderSupport中,而location只有 setter方法。类似于这样的配置,在spring的源程序中很常见的。
PropertyPlaceholderConfigurer如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找。
我们可以通过System.setProperty(key, value)或者java中通过-Dnamevalue来给Spring配置文件传递参数。
四、spring属性占位符
在bean定义时设置bean属性的值时,除了设置固定的值,还可以通过EL表达式和占位符来设置,容器在解析bean定义时会对EL表达式和占位符进行解析求值。本篇来学习一下通过占位符定义属性的用法。
占位符的取值范围有三个:系统变量(System.getProperty)、坏境变量(System.getEnv)、自定义的Properties文件。
Spring提供了三种方式来配置加载自定义的properties:
1、PropertyPlaceholderConfigurer
一个BFPP,通过location属性把properties文件的路径传入,并且可以设置系统变量加载模式,有三种:0、不检查系统属性;1、优先加载自定义属性,加载不到时加载系统属性;2、优先加载系统属性,加载不到时再加载自定义属性。通过systemPropertiesMode设置加载模式。占位符的前后缀默认是${和},但是可以通过设置placeholderPrefix和placeholderSuffix来修改前后缀。PropertyPlaceholderConfigurer的用法如下:
bean定义xml:
- <bean
- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="location">
- <value>classpath:spring/beans/placeholder/attr.properties</value>
- </property>
- <property name="placeholderPrefix" value="${"></property>
- <property name="placeholderSuffix" value="}"></property>
- </bean>
- <bean id="placeHolderBean" class="spring.beans.placeholder.PlaceHolderBean">
- <property name="id" value="${placeholder.id}"></property>
- <property name="name" value="${placeholder.name}"></property>
- <property name="refBean" ref="${placeholder.ref}"></property>
- </bean>
- <bean id="refedBean" class="spring.beans.placeholder.RefedBean"></bean>
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:spring/beans/placeholder/attr.properties</value>
</property>
<property name="placeholderPrefix" value="${"></property>
<property name="placeholderSuffix" value="}"></property>
</bean>
<bean id="placeHolderBean" class="spring.beans.placeholder.PlaceHolderBean">
<property name="id" value="${placeholder.id}"></property>
<property name="name" value="${placeholder.name}"></property>
<property name="refBean" ref="${placeholder.ref}"></property>
</bean>
<bean id="refedBean" class="spring.beans.placeholder.RefedBean"></bean>
自定义属性文件attr.properties:
- placeholder.id = 123
- placeholder.name = cyy
- placeholder.ref = refedBean
placeholder.id = 123
placeholder.name = cyy
placeholder.ref = refedBean
2、PropertySourcesPlaceholderConfigurer
和PropertyPlaceholderConfigurer类似,但是它不能设置系统属性加载模式,而是通过localOverride属性来决定是否用自定义的属性来覆盖系统属性。
3、context:property-placeholder标签
spring设计了context:property-placeholder标签来简化配置,它的原理跟上面两种方式是一样的,通过location属性设置属性文件路径,如有多个用逗号分隔。容器通过PropertyPlaceholderBeanDefinitionParser解析该标签,在解析时会把这个标签解析成PropertyPlaceholderConfigurer或PropertySourcesPlaceholderConfigurer,当system-properties-mode设置为ENVIRONMENT系统变量优先时使用PropertySourcesPlaceholderConfigurer,否则使用PropertyPlaceholderConfigurer,下面是这个标签的用法:
- <context:property-placeholder
- location="classpath:spring/beans/placeholder/attr.properties" />
- <bean id="placeHolderBean" class="spring.beans.placeholder.PlaceHolderBean">
- <property name="id" value="${placeholder.id}"></property>
- <property name="name" value="${placeholder.name}"></property>
- <property name="refBean" ref="${placeholder.ref}"></property>
- </bean>
- <bean id="refedBean" class="spring.beans.placeholder.RefedBean"></bean>
<context:property-placeholder
location="classpath:spring/beans/placeholder/attr.properties" />
<bean id="placeHolderBean" class="spring.beans.placeholder.PlaceHolderBean">
<property name="id" value="${placeholder.id}"></property>
<property name="name" value="${placeholder.name}"></property>
<property name="refBean" ref="${placeholder.ref}"></property>
</bean>
<bean id="refedBean" class="spring.beans.placeholder.RefedBean"></bean>
属性配置器在加载完自定义属性之后会创建一个字符值处理器StringValueResolver,并且把加载到的属性设置到这个处理器中,遍历当前容器中所有的bean定义,使用这个处理来处理属性中未处理的占位符以及别名中的占位符,并且把该处理添加到容器中,为后面的bean解析提供占位符处理,见PlaceholderConfigurerSupport类中doProcessProperties方法的代码:
- protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
- StringValueResolver valueResolver) {
- BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
- String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
- for (String curName : beanNames) {
- // Check that we're not parsing our own bean definition,
- // to avoid failing on unresolvable placeholders in properties file locations.
- if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
- BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
- try {
- visitor.visitBeanDefinition(bd);
- }
- catch (Exception ex) {
- throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
- }
- }
- }
- // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
- beanFactoryToProcess.resolveAliases(valueResolver);
- // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
- beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
- }
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}