Spring表达式语言
初次遇见@Value注解的时候,总以为它的功能非常生硬,只能注入硬编码的字面量值。后来随着学习的深入,渐渐发现@Value注解也能注入属性文件的值,非常好用。让人格外惊喜的是,@Value注解并未止步于此,还提供了通过Spring表达式语言(Spring Expression Language, SpEL)注入更加丰富的值的支持。比如,com.dream包定义了这样的类:
1 public class MusicWorld { 2 public static final String DREAM = "Dream"; 3 }
则可这样把SpEL表达式的运算结果注入Bean中:
1 @Component 2 public class Music { 3 private String musicName = null; 4 5 @Value("#{T(com.dream.MusicWorld).DREAM + ' work'}") 6 public void setMusicName(String musicName) { 7 this.musicName = musicName; 8 } 9 10 // 省略getter方法 11 }
可以看到musicName属性带有@Value注解,其值是括在 #{} 里的SpEL表达式 T(com.dream.MusicWorld).DREAM + ' work' 。这样,Spring容器瞧见@Value注解之后就能解析SpEL表达式,把静态常量MusicWord.DREAM_MUSIC与字符串 work 连接之后得到字符串 Dream work 注入musicName属性里了。
非常明显,Spring表达式语言能够使用表达式描述值的注入,告诉Spring容器在运行时解析表达式之后把解析出来的结果注入Bean中。SpEL表达式必须括在 #{} 里,既可调用类的静态成员,Bean的实例成员;也可使用各种运算符进行值的运算。功能非常强大,语法非常简洁,用法非常方便,大大丰富了值的注入。现在,让我们一起瞧瞧SpEL表达式支持的功能。
SpEL表达式与字面量值
这是最简单的一种SpEL表达式。只要把硬编码的数字,布尔值,字符串等字面量值括在 #{} 里,就能通过SpEL表达式注入字面量值了。其中,字符串需要括在两个单引号 ' 里。如果需要注入的字符串本身就有单引号,可用两个单引号 '' 表示。以下示例就注入了字符串 One's dream :
1 @Component 2 public class Music { 3 private String musicName = null; 4 5 @Value("#{'One''s dream'}") 6 public void setMusicName(String musicName) { 7 this.musicName = musicName; 8 } 9 10 // 省略getter方法 11 }
SpEL表达式与集合
SpEL表达式支持集合的注入。如果集合的类型是Array,List或Set,可把集合的值使用逗号隔开之后括在 {} 里。如下所示:
1 @Component 2 public class Music { 3 private String[] musicNameArray = null; 4 private List<String> musicNameList = null; 5 private Set<String> musicNameSet = null; 6 7 @Value("#{{'One', 'Two', 'Three'}}") 8 public void setMusicNameArray(String[] musicNameArray) { 9 this.musicNameArray = musicNameArray; 10 } 11 12 @Value("#{{'One', 'Two', 'Three'}}") 13 public void setMusicNameList(List<String> musicNameList) { 14 this.musicNameList = musicNameList; 15 } 16 17 @Value("#{{'One', 'Two', 'Three'}}") 18 public void setMusicNameSet(Set<String> musicNameSet) { 19 this.musicNameSet = musicNameSet; 20 } 21 22 // 省略getter方法 23 }
如果集合的类型是Map,能以 键: 值 表示一对键值,而后把多对键值使用逗号隔开之后括在 {} 里。如下所示:
1 @Component 2 public class Music { 3 private Map<String, String> musicNameMap = null; 4 5 @Value("#{{OneKey: 'One', TwoKey: 'Two', ThreeKey: 'Three'}}") 6 public void setMusicNameMap(Map<String, String> musicNameMap) { 7 this.musicNameMap = musicNameMap; 8 } 9 10 // 省略getter方法 11 }
访问Bean的属性
SpEL表达式支持以 Bean的ID.Bean的属性名 这样的方式访问Spring容器里的Bean的属性。假如com.dream包定义了这样的组件:
1 @Component("music") 2 public class Music { 3 private String musicName = null; 4 5 @Value("Dream") 6 public void setMusicName(String musicName) { 7 this.musicName = musicName; 8 } 9 10 // 省略getter方法 11 }
则可这样访问ID为music的Bean的musicName属性,把它的值注入player中:
1 @Component("player") 2 public class Player { 3 private String playingMusicName = null; 4 5 @Value("#{music.musicName}") 6 public void setPlayingMusicName(String playingMusicName) { 7 this.playingMusicName = playingMusicName; 8 } 9 10 // 省略getter方法 11 }
访问Array,List,Set的值
SpEL表达式支持通过中括号 [] 括起索引访问Array,List,Set的值。假如com.dream包定义了这样的组件:
1 @Component("music") 2 public class Music { 3 private String[] musicNameArray = null; 4 private List<String> musicNameList = null; 5 private Set<String> musicNameSet = null; 6 7 @Value("#{{'One', 'Two', 'Three'}}") 8 public void setMusicNameArray(String[] musicNameArray) { 9 this.musicNameArray = musicNameArray; 10 } 11 12 @Value("#{{'One', 'Two', 'Three'}}") 13 public void setMusicNameList(List<String> musicNameList) { 14 this.musicNameList = musicNameList; 15 } 16 17 @Value("#{{'One', 'Two', 'Three'}}") 18 public void setMusicNameSet(Set<String> musicNameSet) { 19 this.musicNameSet = musicNameSet; 20 } 21 22 // 省略getter方法 23 }
则可这样指定索引取出Array,List,Set的值,把它们注入player中:
1 @Component("player") 2 public class Player { 3 private String playingMusicName_1 = null; 4 private String playingMusicName_2 = null; 5 private String playingMusicName_3 = null; 6 7 @Value("#{music.musicNameArray[0]}") 8 public void setPlayingMusicName_1(String musicName) { 9 this.playingMusicName_1 = musicName; 10 } 11 12 @Value("#{music.musicNameList[0]}") 13 public void setPlayingMusicName_2(String musicName) { 14 this.playingMusicName_2 = musicName; 15 } 16 17 @Value("#{music.musicNameSet[0]}") 18 public void setPlayingMusicName_3(String musicName) { 19 this.playingMusicName_3 = musicName; 20 } 21 22 // 省略getter方法 23 }
访问Map的值
SpEL表达式支持通过中刮号 [] 括起Map的键访问Map的值。假如com.dream包定义了这样的组件:
1 @Component("music") 2 public class Music { 3 private Map<String, String> musicNameMap = null; 4 5 @Value("#{{OneKey: 'One', TwoKey: 'Two', ThreeKey: 'Three'}}") 6 public void setMusicNameMap(Map<String, String> musicNameMap) { 7 this.musicNameMap = musicNameMap; 8 } 9 10 // 省略getter方法 11 }
则可这样指定Map的键取出Map的值,把它注入player中:
1 @Component("player") 2 public class Player { 3 private String playingMusicName = null; 4 5 @Value("#{music.musicNameMap['OneKey']}") 6 public void setPlayingMusicName(String playingMusicName) { 7 this.playingMusicName = playingMusicName; 8 } 9 10 // 省略getter方法 11 }
调用Bean的方法
SpEL表达式支持通过 Bean的ID.Bean的方法 这样的方式调用Spring容器里的Bean的方法。假如com.dream包定义了这样的组件:
1 @Component("music") 2 public class Music { 3 private String musicName = null; 4 5 @Value("Dream") 6 public void setMusicName(String musicName) { 7 this.musicName = musicName; 8 } 9 10 public String appendMusicName(String appendValue) { 11 return this.musicName + appendValue; 12 } 13 14 // 省略getter方法 15 }
则可这样调用ID为music的Bean的appendMusicName方法,把它的返回值注入player中:
1 @Component("player") 2 public class Player { 3 private String playingMusicName = null; 4 5 @Value("#{music.appendMusicName(' work')}") 6 public void setPlayingMusicName(String playingMusicName) { 7 this.playingMusicName = playingMusicName; 8 } 9 10 // 省略getter方法 11 }
T()运算符
SpEL表达式提供了T()运算符用于获取类的Class对象。只要把全限定类名指给T()运算符,T()运算符即能获取指定类的Class对象。因此,假如com.dream包定义了这样的类:
1 public class MusicWorld { 2 public static final String DREAM = "Dream"; 3 }
则可这样通过SpEL表达式获取MusicWorld的Class对象,把它注入music中:
1 @Component("music") 2 public class Music { 3 private Class musicWorldClass = null; 4 5 @Value("#{T(com.dream.MusicWorld)}") 6 public void setMusicWorldClass(Class musicWorldClass) { 7 this.musicWorldClass = musicWorldClass; 8 } 9 10 // 省略getter方法 11 }
一种常用的用法是使用T()运算符获取类的Class对象之后访问类的静态成员。如下所示:
1 @Component("music") 2 public class Music { 3 private String musicName = null; 4 5 @Value("#{T(com.dream.MusicWorld).DREAM}") 6 public void setMusicName(String musicName) { 7 this.musicName = musicName; 8 } 9 10 // 省略getter方法 11 }
matches运算符
SpEL表达式支持通过matches运算符,以 字符串 matches 正则表达式 这样的方式判断某个字符串是否匹配某个正则表达式:如果匹配,则返回TRUE;否则返回FALSE。因此,假如com.dream包定义了这样的组件:
1 @Component("music") 2 public class Music { 3 private String musicName = null; 4 5 @Value("Dream") 6 public void setMusicName(String musicName) { 7 this.musicName = musicName; 8 } 9 10 // 省略getter方法 11 }
则可这样在SpEL表达式里使用matches运算符,看看music的musicName属性的值是不是只有字母,并把运算结果注入player中:
1 @Component("player") 2 public class Player { 3 private boolean isMusicValid = false; 4 5 @Value("#{music.musicName matches '^[a-zA-Z]+$'}") 6 public void setIsMusicValid(boolean isMusicValid) { 7 this.isMusicValid = isMusicValid; 8 } 9 10 // 省略getter方法 11 }
new运算符
SpEL表达式支持通过new运算符,以 new 带有全限定类名的构造函数 这样的方式创建类的实例。假如com.dream包定义了这样的类:
1 public class Music { 2 private String musicName = null; 3 4 public Music(String musicName) { 5 this.musicName = musicName; 6 } 7 }
则可这样在SpEL表达式里使用new运算符创建Music类的实例,把实例注入player中:
1 @Component("player") 2 public class Player { 3 private Music playingMusic = null; 4 5 @Value("#{new com.dream.Music('Dream')}") 6 public void setMusic(Music playingMusic) { 7 this.playingMusic = playingMusic; 8 } 9 10 // 省略getter方法 11 }
关系运行符
SpEL表达式提供了这些用于进行关系运算的关系运算符:
1.小于:< (也可使用文本lt)
2.大于:> (也可使用文本gt)
3.小于等于:<= (也可使用文本le)
4.大于等于:>= (也可使用文本ge)
5.等于:== (也可使用文本eq)
6.不等于:!= (也可使用文本ne)
这些关系运算符的用法与Java的关系运算符是一样,大家一瞧便知,不再详叙。
逻辑运算符
SpEL表达式提供了这些用于进行逻辑运算的逻辑运算符:
1.与:and
2.或:or
3.非:not
这些逻辑运算符的用法与Java的逻辑运算符是一样,大家一瞧便知,不再详叙。
算数运算符
SpEL表达式提供了这些用于进行算数运算的算数运算符:
1.数字的相加或字符串的相连:+
2.减:-
3.乘:*
4.除:/
5.取模:%
6.乘方:^
这些算术运算符的用法与Java的算术运算符是一样,大家一瞧便知,不再详叙。
?:运算符
SpEL表达式提供的?:运算符具有两种用法:一种是作为三元运算符(Ternary Operator)用于If-Then-Else的条件运算;一种是作为Elvis运算符用于简化三元运算符关于null值的用法。
?:运算符用作三元运算符时,它的用法和Java的?:运算符是一样的。如下所示:
1 #{music.musicName != null ? music.musicName: 'There is not any music.'}
这条SpEL表达式会先检查 music.musicName 的值是否不等于null:如果不等于null,则返回 music.musicName 的值;否则返回字符串 There is not any music.
另外,这条SpEL表达式还能使用Elvis运算符这样简化:
1 #{music.musicName ?: 'There is not any music.'}
Elvis运算符会先检查 music.musicName 的值是否不等于null:如果不等于null,则直接返回自己,也就是返回
music.musicName 的值;否则返回 ?: 后面的值,也就是返回字符串 There is not any music.
?.运算符
SpEL表达式提供了?.运算符用于安全地访问Bean的属性,调用Bean的方法。按照以往的写法,我们总是这样访问Bean的属性,调用Bean的方法:
1 #{music.musicName} 2 #{music.appendMusicName(' work')}
毫无疑问,如果music等于null,程序定会抛出异常。为了避免这样的错误,我们可用?.运算符修改如下:
1 #{music?.musicName} 2 #{music?.appendMusicName(' work')}
我们把 . 换成了?.运算符。这样,当music等于null时,这条SpEL表达式只会返回null,不会抛出异常;当music不等于null时,这条SpEL表达式才会访问Bean的属性,调用Bean的方法。
至此,关于SpEL表达式的介绍该告一段落了。当然,XML配置文件也是支持SpEL表达式的,而且用法与@Value注解一模一样。大家一看下面的示例就知道了:
1 <bean id="music_1" class="com.dream.Music"> 2 <property name="musicName" 3 value="#{T(com.dream.MusicWorld).DREAM + ' work'}" /> 4 </bean> 5 <bean id="music_2" class="com.dream.Music"> 6 <constructor-arg type="java.lang.String" 7 value="#{T(com.dream.MusicWorld).DREAM + ' work'}" /> 8 </bean>
下章,我们将会开始介绍混合配置。欢迎大家继续阅读,谢谢大家!