运行时值注入Bean
bean装配的另外一个方面指的是将一个值注入到bean的属性或者构造器参数中。但如果是下面这种装配bean的话注入的值就是硬编码了。
@Bean
public Student student() {
return new Student(
"zhengjunming",
"3116005120");
}
使用XML装配bean也同样是硬编码
<bean id="student" class="com.zheng.spring.Student"
c:_name="zhengjunming"
c:_studentId="3116005120" />
有时候硬编码是可以的,但有的时候,我们可能会希望避免硬编码值,而是想让这些值在运行时再确定。为了实现这些功能,Spring提供了两种在运行时求值的方式:
- 属性占位符(Property placeholder)。
- Spring表达式语言(SpEL)。
注入外部的值
属性占位符
使用@PropertySource注解和Environment
这种方法就在一定程度上消除了硬编码的情况
package com.zheng.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
@Configuration
@PropertySource("classpath:student.properties") // 声明属性源
public class TestProperty {
private final Environment env;
@Autowired
public TestProperty(Environment env) {
this.env = env;
}
@Bean
public Student student() {
// 利用Environment对象检索属性值
return new Student(env.getProperty("student.name"),
env.getProperty("student.id"));
}
}
student.properties
内容如下
student.name=zhengjunming
student.id=3116005120
深入Environment
getProperty()方法由四个重载的形式
String getProperty(String key);
String getProperty(String key, String defaultValue);
<T> T getProperty(String key, Class<T> type);
<T> T getProperty(String key, Class<T> type, T defaultValue);
我们首先看第二个重载的方法,根据参数我们可以很清楚的知道,如果指定属性不存在的时候,会使用一个默认值defaultValue。
@Bean
public Student student() {
// 利用Environment对象检索属性值
return new Student(env.getProperty("student.name", "zheng"),
env.getProperty("student.id", "3116005120"));
}
当我们希望得到的是Integer类型或者其他类型而不是String类型,我们就可以用到第三个和第四个的getProperty()方法。
int id = env.getProperty("student.id", Integer.class, 3116005120);
Environment还提供了另外几个与属性相关的方法:
- getRequiredProperty()方法:属性必须定义:不然抛出IllegalStateException异常。
@Bean
public Student student() {
return new Student(env.getRequiredProperty("student.name"),
env.getRequiredProperty("student.id"));
}
- containsProperty()方法:检查某个属性是否存在。
- getPropertyAsClass()方法:将属性解析为类
除了属性相关的功能以外,Environment还提供了一些方法来检查哪些profile处于激活状态:
- String[] getActiveProfiles():返回激活profile名称的数组;
- String[] getDefaultProfiles():返回默认profile名称的数组;
- boolean acceptsProfiles(String… profiles):如果environment支持给定profile的话,就返回true。
解析属性占位符
解析占位符的形式为使用“${ … }”包装的属性名称。
在XML中解析占位符
<bean id="student" class="com.zheng.spring.Student"
c:_name="${student.name}"
c:_id="${student.id}" />
如果我们依赖于组件扫描和自动装配来创建和初始化应用组件的话,那么就没有指定占位符的配置文件或类了。在这种情况下,我们可以使用@Value注解。
public Student(@Value("$student.name") String name,
@Value("$student.id") String id) {
this.name = name;
this.id = id;
}
为了使用占位符,我们必须要配置一个PropertyPlaceholderConfigurer bean或PropertySourcesPlaceholderConfigurer bean。从Spring 3.1开始,推荐使用PropertySourcesPlaceholderConfigurer,因为它能够基于Spring Environment及其属性源来解析占位符。
@Bean
public PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
对应的XML配置如下
<context:property-placeholder />
使用Spring表达式语言(SpEL)进行装配
SpEL拥有很多特性,包括:
- 使用bean的ID来引用bean;
- 调用方法和访问对象的属性;
- 对值进行算术、关系和逻辑运算;
- 正则表达式匹配;
- 集合操作。
SpEL表达式要放到“#{ … }”之中,例如#{T(System).currentTimeMillis()}
: T()表达式会将java.lang.System视为Java中对应的类型,因此可以调用其static修饰的currentTimeMillis()方法。
SpEL表达式也可以引用其他的bean或其他bean的属性。例如,如下的表达式会计算得到ID为student的bean的name属性:#{student.name}
通过systemProperties
对象引用系统属性:#{systemProperties['student.name']}
如果通过组件扫描创建bean的话,在注入属性和构造器参数时,我们可以使用@Value注解。
public Student(@Value("#{systemProperties['student.name']") String name,
@Value("#{systemProperties['student.id']") String id) {
this.name = name;
this.id = id;
}
引用bean、属性和方法
student为bean的ID
- 引用bean:
#{student}
- 引用属性:
#{student.name}
- 引用方法:
#{student.getName()}
- 操作方法返回值:
#{student.getName().toUpperCase()}
。则利用了返回值,然后使字符串变为大写。 - 若上面返回值为null将引发空指针异常,为了避免这种情况发生,可以使用类型安全的运算符:
#{student.getName()?.toUpperCase()}
。如果为null将不会引用toUpperCase的方法,只会用到返回值null。
在表达式中使用类型
要在SpEL中访问类作用域的方法和常量的话,要依赖T()这个运算符。例如:#{T(java.lang.Math)}
T()运算符的结果会是一个Class对象。能够访问目标类型的静态方法和常量。例如:#{T(java.lang.Math).PI}
用来操作表达式值的SpEL运算符
运 算 符 类 型 | 运 算 符 |
---|---|
算 术 运 算 |
|
比较运算 | < 、 > 、 == 、 <= 、 >= 、 lt 、 gt 、 eq 、 le 、 ge |
逻辑运算 | and 、 or 、 not 、│ |
条件运算 | ?: (ternary) 、 ?: (Elvis) |
正则表达式 | matches |