maven占位符的使用(认真看完你就明白也就这么回事)
在我们maven项目中经常用到占位符,除了spring配置文件中用到,java代码中也常常引用占位符,而到底占位符是怎么使用的,接下来我们进行简单的剖析!
(一)maven配置文件pom.xml中配置占位符
maven占位符默认的是${},也可以自己指定,如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<useDefaultDelimiters>false</useDefaultDelimiters>
<delimiters>
<delimiter>$[*]</delimiter>
</delimiters>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
其中configuration中的设置就是指定占位符使用的方式:
useDefaultDelimiters |
是否使用默认占位符 |
delimiter |
占位符 |
encoding | 占位符中value的编码方式 |
先举个简单例子:
如果applicationContext.xml配置文件里面配置如下:
<bean id="senderKafka" class="com.*.epp.kafka.SendKafka">
<property name="metadataBookerList" value="$[metadata.broker.list]" />
<property name="requestRequiredAcks" value="${request.required.acks}" />
<property name="serializerClass" value="${serializer.class}" />
</bean>
我们给定一个app.properties参数文件如下:
metadata.broker.list = 10.27.25.161:9092,10.27.25.163:9093
request.required.acks = requestRequiredAcks
serializer.class = Serializer
运行build.xml之后,上面配置信息变成
<bean id="senderKafka" class="com.*.epp.kafka.SendKafka">
<property name="metadataBookerList" value="10.27.25.161:9092,10.27.25.163:9093" />
<property name="requestRequiredAcks" value="${request.required.acks}" />
<property name="serializerClass" value="${serializer.class}" />
</bean>
通过$[]占位符,metadata.broker.list的值就被替换掉了,而另外两个由于默认的占位符失效了,所以没能被替换!
如果applicationContext.xml加载app.properties文件添加如下:
<context:property-placeholder location="app.properties"/>
再次执行build.xml,上面配置信息如下:
<bean id="senderKafka" class="com.*.epp.kafka.SendKafka">
<property name="metadataBookerList" value="10.27.25.161:9092,10.27.25.163:9093" />
<property name="requestRequiredAcks" value="requestRequiredAcks" />
<property name="serializerClass" value="Serializer" />
</bean>
此时三个bean的value均被替换了,那么你会疑问了,这是什么道理?
其实,pom中定义的占位符$[]是直接由maven执行处理了,
而${request.required.acks}和${serializer.class}会由spring读取app.properties来赋值的。
(二)占位符在maven项目中的实际使用
- spring配置文件通过加载properties文件使用默认占位符${}引入参数
- 代码中通过@Value注解读取properties的属性值
如下图,在实际开发中不管是spring配置文件还是java代码中引用参数都不会直接引用父模块下的properties文件,而是通过在子模块下创建一个properties文件,创建的properties文件通过maven设置的占位符$[]引用父模块下properties文件,子模块下创建的properties文件用于该模块下动态数据的引用。如下图:
从图中还可得知:maven占位符除了可以引用peoperties文件中的参数,还可以引用pom文件下的properties参数
这一步主要是子模块需要的参数从父模块的properties文件引用到子模块的properties文件中!
(三) properties文件的使用
1、Spinrg的Bean XML定义中,可以通过${属性名}使用properties文件配置的值
首先,必须先加载properties配置文件,方式有两种,如下
方式一
<!--
用途1:Spring的xml配置文件中,可以通过${属性名}使用properties文件配置的值
用途2:可以使用@Value("${属性名}")注解读取properties文件配置的值,再给字段赋值
方法1:注解在字段上,给字段赋值
方法2:注解在字段的setter方法中赋值
-->
<context:property-placeholder location="classpath:jdbc.properties"/>
方式二
<!--
用途1:Spring的xml配置文件中,可以通过${属性名}使用properties文件配置的值
用途2:可以使用@Value("${属性名}")注解读取properties文件配置的值,再给字段赋值
方法1:注解在字段上,给字段赋值
方法2:注解在字段的setter方法中赋值
-->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>/WEB-INF/configs/sqlServer.properties</value>
</property>
</bean>
可以清楚的看到,方式一,非常地简洁,但是如果要使用多个properties就可能实现不了,其实可以通过通配符实现,会有点麻烦。
接着,就可以在Bean的定义中,使用properties中的属性值,如下
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.driver}</value>
</property>
<property name="url" >
<value>${jdbc.url}</value>
</property>
<property name="username" >
<value>${jdbc.user}</value>
</property>
<property name="password">
<value>${jdbc.pwd}</value>
</property>
</bean>
2、使用@Value注解读取properties文件配置的值,再给字段赋值
@Value注解是Spring 3.0 之后引入的新特性 @Value的值有三种类型,#{} 、${} 和 #{'${}'} ,其实是#{}和${}这两种类型,#{'${}'} 这种是前两种的嵌套使用,下面分别介绍 |
1) #{expression?:default value}
#{} 花括号里面的是SpEL表达式(即Spring Expression Language),?: 前面的是表达式,?: 后面的是默认值,这种方式非常地灵活,可以直接取bean对象的字段值!SpEL表达式的介绍,请看官方参考资料http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html
但是,这种方式下,有个缺陷,那就是 properties配置文件中的属性名称不能带点,否则取不到值,会报错
如 file.uploadpath = E:\\360Downloads\\temp , 读取该属性值,就会报错,如下
@Value("#{prop.file.uploadpath}")
private String uploadPath;
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'file' cannot be found on object of type 'java.util.Properties' - maybe not public?
要使用这种类型的Value值,实现方式有两种,如下
方式一:
<!--
用途:可以使用@Value("#{prop.属性名}")注解读取properties文件配置的值,再给字段赋值
方法1:注解在字段上,给字段赋值
方法2:注解在字段的setter方法中赋值
注意:@Value("#{prop.属性名}") 中的 prop 是 注册的PropertiesFactoryBean的 Bean ID
-->
<bean id="prop" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<array>
<value>classpath:fileupload.properties</value>
</array>
</property>
</bean>
方式二:
<!--
用途:可以使用@Value("#{prop.属性名}")注解读取properties文件配置的值,再给字段赋值
方法1:注解在字段上,给字段赋值
方法2:注解在字段的setter方法中赋值
注意:@Value("#{prop.属性名}") 中的 prop 是 注册的PropertiesFactoryBean的 Bean ID
-->
<util:properties id="prop" location="classpath:fileupload.properties"/>
可以清楚的看到,方式二,非常地简洁,但是如果要使用多个properties就可能实现不了,其实可以通过通配符实现,会有点麻烦。
接下来,看demo
fileupload.properties文件:
name=zengyanhui
age=12
Test.java:
package edu.mvcdemo.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* @编写人: yh.zeng
* @编写时间:2017-7-26 下午11:04:10
* @文件描述: todo
*/
@Component("test")
@Scope("singleton")
public class Test {
@Value("#{prop.name}")
private String name;
@Value("#{prop.age}")
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
SpringBeanUtilsTest.java:
package edu.mvcdemo.utils;
import edu.mvcdemo.service.Test;
import junit.framework.TestCase;
/**
* @编写人: yh.zeng
* @编写时间:2017-7-26 下午11:09:38
* @文件描述: todo
*/
public class SpringBeanUtilsTest extends TestCase{
public void test1(){
SpringBeanUtils.setFilePath("src/springCfg/applicationContext-base.xml");
Test test = (Test) SpringBeanUtils.getBean("test");
System.out.println("name="+test.getName());
System.out.println("age="+test.getAge());
}
}
程序运行结果:
[INFO][2017-07-27 23:50:59][AbstractApplicationContext:583] - Refreshing org.[email protected]b8d805: startup date [Thu Jul 27 23:50:59 CST 2017]; root of context hierarchy
[INFO][2017-07-27 23:50:59][XmlBeanDefinitionReader:317] - Loading XML bean definitions from file [D:\EclipseWorkspace\MavenSpringMvcDemo\src\springCfg\applicationContext-base.xml]
name=zengyanhui
age=12
2)${property:default value}
${}这种值,只用来读取properties配置文件中的属性值, : 前面的是属性名称,: 后面的是默认值。这种类型的值,却可以读取带点的属性值,如 file.uploadpath = E:\\360Downloads\\temp,可以使用@Value("${file.uploadpath}")读取
要使用这种方式的Value,有两种实现方式,如下
方式一:
<!--
用途1:Spring的xml配置文件中,可以通过${属性名}使用properties文件配置的值
用途2:可以使用@Value("${属性名}")注解读取properties文件配置的值,再给字段赋值
方法1:注解在字段上,给字段赋值
方法2:注解在字段的setter方法中赋值
-->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:fileupload.properties</value>
</property>
</bean>
方式二:
<!--
用途1:Spring的xml配置文件中,可以通过${属性名}使用properties文件配置的值
用途2:可以使用@Value("${属性名}")注解读取properties文件配置的值,再给字段赋值
方法1:注解在字段上,给字段赋值
方法2:注解在字段的setter方法中赋值
-->
<context:property-placeholder location="classpath:fileupload.properties"/>
可以清楚的看到,方式二,非常地简洁,但是如果要使用多个properties就可能实现不了,其实可以通过通配符实现,会有点麻烦。
下面看demo:
package edu.mvcdemo.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* @编写人: yh.zeng
* @编写时间:2017-7-26 下午11:04:10
* @文件描述: todo
*/
@Component("test")
@Scope("singleton")
public class Test {
@Value("${name}")
private String name;
@Value("${age}")
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
程序运行结果:
[INFO][2017-07-27 23:50:59][AbstractApplicationContext:583] - Refreshing org.[email protected]b8d805: startup date [Thu Jul 27 23:50:59 CST 2017]; root of context hierarchy
[INFO][2017-07-27 23:50:59][XmlBeanDefinitionReader:317] - Loading XML bean definitions from file [D:\EclipseWorkspace\MavenSpringMvcDemo\src\springCfg\applicationContext-base.xml]
name=zengyanhui
age=12
(3)#{'${}'}
这种类型的Value值,是#{}里面嵌套${}使用,所以必须按照上述的(1)(2)两种类型的实现方式,配置properties文件,才可以使用这种方式的值
@Value("#{'${age}'}")
private String age;
下面贴一张工作中使用的方式,大家可以研究研究