Spring高级装配之运行时注入

2016-08-17 高悦翔 更多博文 » 博客 » GitHub »

原文链接 http://blog.gaoyuexiang.cn/Spring%E9%AB%98%E7%BA%A7%E8%A3%85%E9%85%8D%E4%B9%8B%E8%BF%90%E8%A1%8C%E6%97%B6%E6%B3%A8%E5%85%A5/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


  • {:toc #zhuru}

运行时注入与硬编码注入是相对的。硬编码注入在编译时就已经确定了,运行时注入则可能需要一些外部的参数来解决。

Spring提供的两种在运行时求值的方式:

  • 属性占位符(Property placeholder)
  • Spring表达式语言(SpEL)

注入外部的值

使用@PropertySource注解可以引入.properties文件,使用其中的值。

@Configuration
@PropertySource("classpath:jdbc.properties")
public class JDBCConfig {
    @Autowired
    Environment env;

    @Bean
    public DataSource dataSource() {
        env.getProperties("driver");
        ...
    }
}

深入了解Spring中的Environment

上例的Environment有如下方法获取属性

  • String getProperty(String key);
  • String getProperty(String key, String defaultValue);
  • T getProperty(String key, Class type);
  • T getProperty(String key, Class type, T defaultValue);

这几个重载方法的作用顾名思义。其中第一、三个方法获取一个不存在的属性时,会抛出IllegalStateException异常。

可以使用containsProperty(String key)方法查看是否存在某个属性。

其他相关方法:

  • Class<T> getPropertyAsClass(String key, Class<T> targetType) : 将获取的属性转换为类
  • String[] getActiveProfiles() : 返回激活profile名称的数组
  • String[] getDefaultProfiles() : 返回默认profile名称的数组
  • boolean acceptsProfiles(String... profiles) : 如果environment支持给定的profile,则返回true

解析属性占位符

使用占位符,可将属性定义到外部的.properties文件中,然后使用占位符插入到bean中。占位符使用${...}包装属性名称。

Java配置中使用@Value注解。

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

使用占位符必须配置一个PropertySourcesPlaceholderConfigurer bean,它能够基于Spring Environment及其属性来解析占位符。

@Bean
public PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

使用Spring表达式语言进行装配

SpEL主要特性:

  • 使用beanID来引用bean
  • 访问对象的属性和方法
  • 可对值进行算数、关系和逻辑运算
  • 正则表达式匹配
  • 集合操作

SpEL还可以用在DI之外的地方

SpEL样例

SpEL表达式要放在#{ ... }中,里面的"..."就是SpEL表达式。

  • #{1}

常量,结果始终为1

  • #{T(System).currentTimeMillis()}

T()表达式会将java.lang.System视为Java中对应的类型,然后调用其方法,获取当前时间戳。

  • #{dataSource.user}

dataSource为声明的其他bean,这里可以获取它的属性user

  • #{systemProperties['username']}

通过systemProperties对象获取系统属性

表示字面量

可表示的字面量有int,float/double,String,boolean,其中浮点值可以用科学技术法表示:#{6.18E3}

引用bean、属性和方法

引用对象 表达式
bean #{dataSource}
bean's field #{dataSource.user}
bean's method #{dataSource.getPassword()}
bean's method's method #{dataSource.getPassword().toUpperCase()}

如果方法返回值为null,第四种情况会抛出NullPoniterException。可以使用:

#{dataSource.getPassword()?.toUpperCase()}

其中的?.运算符能够在访问前确保不为null,否则返回null

在表达式中使用类型

使用T()表达式来访问Java类中的static方法和常量,在括号内的是类名,返回一个Class对象,然后调用其方法和常量。

SpEL运算符

运算符类型 运算符
算数运算 +, -, *, /, %, ^
比较运算 <, >, ==, <=, >=, lt, gt, eq, le, ge
逻辑运算 and, or, not, \
条件运算 ?: (ternary), ?: (Elvis)
正则表达式 matches
  • Elvis运算符

利用三元运算符来检查场景:#{disc.title ?: 'Rattle and Hum'},当disc.titlenull时,返回"Rattle and Hum"

名称的来历据说是因为'?'长得像猫王的头发。。。 :astonished::astonished::astonished:

  • 正则表达式

正则表达式利用matches来支持正则匹配。

计算集合

  • 引入一个元素 : #{jukebox.songs[4].title}
  • 随机选取 : #{jukebox.songs[T(Math).random() * jukebox.songs.size()].title}
  • String中获得char : #{'This is a test'[2]}
  • 使用.?[]进行过滤,得到符合条件的子集 : #{jukebox.songs.?[artist eq 'Aerosmith']}
  • 使用.^[].$[]进行过滤,得到第一个和最后一个匹配项
  • 使用.![]从集合的每个成员选择特定属性放入新集合中 : #{jukebox.songs.![title]}

最后四个表达式有点像lambda表达式

SpEL的表达式可以相互组合使用。

更多Spring学习笔记:https://github.com/kbyyd24/spring.demo.test/issues