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

240 阅读4分钟

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

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

//代码清单一,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;
  }

}
//代码清单二,使用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。

//代码清单三,从外部注入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接口详解

public interface Environment extends PropertyResolver {

	String[] getActiveProfiles();

	String[] getDefaultProfiles();

	boolean acceptsProfiles(String... profiles);
}

Environment继承自PropertyResolver,并另外添加了几个和Profiles相关的方法,Profiles是用来标记运行环境是开发环境还是生产环境的,更多内容请看我前几天写的这篇读书笔记(Spring in Acton 4 读书笔记之根据开发环境装配 bean)[tantanit.com/springinact…]

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

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的方法,最前面两个

String getProperty(String key);

String getProperty(String key, String defaultValue);

是将字符串原样返回的,带defaultValue的方法指明没有key时,返回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);

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

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

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

String getRequiredProperty(String key) throws IllegalStateException;

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

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

String resolvePlaceholders(String text);

String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

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

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

public BlankDisc(
    @Value("${disc.title}") String title,
    @Value("${disc.artist}") String artist) {
  this.title = title;
  this.artist = artist;
}

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

@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”或扫描下方二维码关注微信公众号,第一时间获取最新文章(^_^)

谈谈IT