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>

下章,我们将会开始介绍混合配置。欢迎大家继续阅读,谢谢大家!

返回目录    下载代码

posted @ 2022-02-16 23:07  林雪波  阅读(699)  评论(0编辑  收藏  举报