Spring in Acton 4 读书笔记之在运行时注入值

字数1,492 大约花费6分钟

目录

  1. 1. 从外部注入值
    1. 1.1. 从 Environment 获取属性值
    2. 1.2. Environment 接口详解
    3. 1.3. 使用属性占位符注入属性值
  2. 2. 使用 Spring 正则表达式注入属性值

Spring in Action(Spring 实战)第三章第五节(3.5 Runtime value injection)讲述运行时注入值,第三章讲述装配 bean 的一些高级专题,之前的章节一直在讨论用 Spring 注入 bean, 实际上,有时候也需要给一些变量注入值。

比如下面的例子其实就是通过构造函数给 BlankDisc 的 title 属性注入值,这里用的是硬编码的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 代码清单一,BlankDisc 类
public class BlankDisc {

private final String title;
private final String artist;

public BlankDisc(String title, String artist) {
this.title = title;
this.artist = artist;
}

public String getTitle() {
return title;
}

public String getArtist() {
return artist;
}

}
1
2
3
4
5
6
7
// 代码清单二,使用 hardcode 注入 title 和 artist
@Bean
public CompactDisc sgtPeppers() {
return new BlankDisc(
"Sgt. Pepper's Lonely Hearts Club Band",
"The Beatles");
}

硬编码常常会引起问题,Spring 提供了以下两种方式,在运行时给变量注入值:

  • 属性占位符(Property placeholders)
  • Spring 正则表达式(Spring Expression Language)

从外部注入值

将属性从外部注入到 Environment,然后显式地从 Environment 获取属性值或者使用属性占位符隐式地从 Environment 获取属性值。

从 Environment 获取属性值

最简单的方式是定义属性,然后通过 Spring 的 Environment 变量获取这些属性。下面的例子演示了如何使用配置文件,从外部注入值,并且装配 bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 代码清单三,从外部注入 title 和 artist
@Configuration
@PropertySource("classpath:/com/soundsystem/app.properties")
public class EnvironmentConfig {

@Autowired
Environment env;

@Bean
public BlankDisc blankDisc() {
return new BlankDisc(
env.getProperty("disc.title"),
env.getProperty("disc.artist"));
}

}

与代码清单二使用硬编码不同,代码清单三,从 Environment 中获取 disc.title 和 disc.artist 属性值,在运行时动态地将值注入给 BlankDisc 的 title 和 artist 属性。其中 @PropertySource 标签指定属性文件的路径。

Environment 接口详解

1
2
3
4
5
6
7
8
public interface Environment extends PropertyResolver {

String[] getActiveProfiles();

String[] getDefaultProfiles();

boolean acceptsProfiles(String... profiles);
}

Environment 继承自 PropertyResolver,并另外添加了几个和 Profiles 相关的方法,Profiles 是用来标记运行环境是开发环境还是生产环境的,更多内容请看我前几天写的这篇读书笔记(Spring in Acton 4 读书笔记之根据开发环境装配 bean)[http://tantanit.com/springinacton4-du-shu-bi-ji-zhi-gen-ju-kai-fa-huan-jing-zhuang-pei-bean/]

PropertyResolver 顾名思义,是用来解析属性的,所以大部分方法名都包含 Property 字样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface PropertyResolver {

boolean containsProperty(String key);

String getProperty(String key);

String getProperty(String key, String defaultValue);

<T> T getProperty(String key, Class<T> targetType);

<T> T getProperty(String key, Class<T> targetType, T defaultValue);

<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);

String getRequiredProperty(String key) throws IllegalStateException;

<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

String resolvePlaceholders(String text);

String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

因为 Environment 继承自 PropertyResolver,所以也包含这些方法。一共有七个名称是或类似 getProperty 的方法,最前面两个

1
2
3
String getProperty(String key);

String getProperty(String key, String defaultValue);

是将字符串原样返回的,带 defaultValue 的方法指明没有 key 时,返回 defaultValue;

而下面三个:

1
2
3
4
5
<T> T getProperty(String key, Class<T> targetType);

<T> T getProperty(String key, Class<T> targetType, T defaultValue);

<T> Class<T> getPropertyAsClass(String key, Class<T> targetType);

是将字符串解析为指定类型的,其中 getPropertyAsClass 返回 Class类型。

上面这五个方法中,不带 defaultValue 的两个,当找不到该属性时,返回 null。

而剩下的两个方法,方法名包含 required 字样,在找不到属性时,将抛出 IllegalStateException 异常:

1
2
3
String getRequiredProperty(String key) throws IllegalStateException;

<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

此外,containsProperty 一目了然,是判断是否包含某属性的。最后,剩下两个用来解析占位符的方法:

1
2
3
String resolvePlaceholders(String text);

String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

使用属性占位符注入属性值

属性占位符的语法形如 ${disc.title},比如下面的例子,将使用 @Value 标签,将 disc.title 和 disc.artist 属性分别注入到 title 和 artist。这种方式,实际上也是从 Environment 获取的,但比从 Environment 直接获取值更加方便,所以是最经常使用的。

1
2
3
4
5
6
public BlankDisc(
@Value("${disc.title}") String title,

@Value("${disc.artist}") String artist) {

this.title = title;
this.artist = artist;
}

要使用属性占位符,需要在 java 配置文件中,加载 PropertySourcesPlaceholderConfigurer 类型的 bean:

1
2
3
4
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}

使用 Spring 正则表达式注入属性值

Spring 正则表达式可以做到:

  • 通过 id 引入 bean
  • 访问对象的属性或执行方法
  • 对值进行数学、关系或逻辑操作
  • 正则表达式匹配
  • 操作列表和集合

语法是形如:#{1}。比如 #{1}表示数值 1,#{T(System).currentTimeMillis()}获取当前时间,#{sgtPeppers.artist}表示 sgtPeppers 对象的 artist 属性等。其实,Spring 正则表达式的用法和 freemarker 的模板语言很像。我(笔者)个人觉得,在后端没有必要使用这么复杂的表达式。对于对象级别的数据,从数据库获取好过用表达式注入。而对于函数操作,使用 java 类库,可读性也比表达式好很多。

实际上,与 spring 正则表达式相比,最常用的还是上面所说的,用 @Value 标签指定属性占位符,注入属性值的方式。

这一章的其它笔记参看

谈谈 IT的文章均为原创或翻译(翻译会注明外文来源),转载请以链接形式标明本文地址: http://tantanit.com/springinacton4-du-shu-bi-ji-zhi-zai-yun-xing-shi-zhu-ru-zhi/

谈谈IT

欢迎关注官方微信公众号获取最新原创文章