Spring表达式语言

初次遇见@Value注解的时候,总以为它的功能非常生硬,只能注入硬编码的字面量值。后来随着学习的深入,渐渐发现@Value注解也能注入属性文件的值,非常好用。让人格外惊喜的是,@Value注解并未止步于此,还提供了通过Spring表达式语言(Spring Expression Language, SpEL)注入更加丰富的值的支持。比如,com.dream包定义了这样的类:

public class MusicWorld {
    public static final String DREAM = "Dream";
}

则可这样把SpEL表达式的运算结果注入Bean中:

@Component
 public class Music {
     private String musicName = null;
 
     @Value("#{T(com.dream.MusicWorld).DREAM + ' work'}")
     public void setMusicName(String musicName) {
         this.musicName = musicName;
     }
 
    // 省略getter方法
}

可以看到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 

@Component
 public class Music {
     private String musicName = null;
 
     @Value("#{'One''s dream'}")
     public void setMusicName(String musicName) {
         this.musicName = musicName;
     }
 
    // 省略getter方法
}

SpEL表达式与集合

SpEL表达式支持集合的注入。如果集合的类型是Array,List或Set,可把集合的值使用逗号隔开之后括在 {} 里。如下所示:

@Component
 public class Music {
     private String[] musicNameArray = null;
     private List<String> musicNameList = null;
     private Set<String> musicNameSet = null;
 
     @Value("#{{'One', 'Two', 'Three'}}")
     public void setMusicNameArray(String[] musicNameArray) {
         this.musicNameArray = musicNameArray;
    }

    @Value("#{{'One', 'Two', 'Three'}}")
    public void setMusicNameList(List<String> musicNameList) {
        this.musicNameList = musicNameList;
    }

    @Value("#{{'One', 'Two', 'Three'}}")
    public void setMusicNameSet(Set<String> musicNameSet) {
        this.musicNameSet = musicNameSet;
    }

    // 省略getter方法
}

如果集合的类型是Map,能以 键: 值 表示一对键值,而后把多对键值使用逗号隔开之后括在 {} 里。如下所示:

@Component
 public class Music {
     private Map<String, String> musicNameMap = null;
 
     @Value("#{{OneKey: 'One', TwoKey: 'Two', ThreeKey: 'Three'}}")
     public void setMusicNameMap(Map<String, String> musicNameMap) {
         this.musicNameMap = musicNameMap;
     }
 
    // 省略getter方法
}

访问Bean的属性

SpEL表达式支持以 Bean的ID.Bean的属性名 这样的方式访问Spring容器里的Bean的属性。假如com.dream包定义了这样的组件:

@Component("music")
 public class Music {
     private String musicName = null;
 
     @Value("Dream")
     public void setMusicName(String musicName) {
         this.musicName = musicName;
     }
 
    // 省略getter方法
}

则可这样访问ID为music的Bean的musicName属性,把它的值注入player中:

@Component("player")
 public class Player {
     private String playingMusicName = null;
 
     @Value("#{music.musicName}")
     public void setPlayingMusicName(String playingMusicName) {
         this.playingMusicName = playingMusicName;
     }
 
    // 省略getter方法
}

访问Array,List,Set的值

SpEL表达式支持通过中括号 [] 括起索引访问Array,List,Set的值。假如com.dream包定义了这样的组件: 

@Component("music")
 public class Music {
     private String[] musicNameArray = null;
     private List<String> musicNameList = null;
     private Set<String> musicNameSet = null;
 
     @Value("#{{'One', 'Two', 'Three'}}")
     public void setMusicNameArray(String[] musicNameArray) {
         this.musicNameArray = musicNameArray;
    }

    @Value("#{{'One', 'Two', 'Three'}}")
    public void setMusicNameList(List<String> musicNameList) {
        this.musicNameList = musicNameList;
    }

    @Value("#{{'One', 'Two', 'Three'}}")
    public void setMusicNameSet(Set<String> musicNameSet) {
        this.musicNameSet = musicNameSet;
    }

    // 省略getter方法
}

则可这样指定索引取出Array,List,Set的值,把它们注入player中: 

@Component("player")
 public class Player {
     private String playingMusicName_1 = null;
     private String playingMusicName_2 = null;
     private String playingMusicName_3 = null;
 
     @Value("#{music.musicNameArray[0]}")
     public void setPlayingMusicName_1(String musicName) {
         this.playingMusicName_1 = musicName;
    }

    @Value("#{music.musicNameList[0]}")
    public void setPlayingMusicName_2(String musicName) {
        this.playingMusicName_2 = musicName;
    }

    @Value("#{music.musicNameSet[0]}")
    public void setPlayingMusicName_3(String musicName) {
        this.playingMusicName_3 = musicName;
    }

    // 省略getter方法
}

访问Map的值

SpEL表达式支持通过中刮号 [] 括起Map的键访问Map的值。假如com.dream包定义了这样的组件:

@Component("music")
 public class Music {
     private Map<String, String> musicNameMap = null;
 
     @Value("#{{OneKey: 'One', TwoKey: 'Two', ThreeKey: 'Three'}}")
     public void setMusicNameMap(Map<String, String> musicNameMap) {
         this.musicNameMap = musicNameMap;
     }
 
    // 省略getter方法
}

则可这样指定Map的键取出Map的值,把它注入player中:

@Component("player")
 public class Player {
     private String playingMusicName = null;
 
     @Value("#{music.musicNameMap['OneKey']}")
     public void setPlayingMusicName(String playingMusicName) {
         this.playingMusicName = playingMusicName;
     }
 
    // 省略getter方法
}

调用Bean的方法

SpEL表达式支持通过 Bean的ID.Bean的方法 这样的方式调用Spring容器里的Bean的方法。假如com.dream包定义了这样的组件:

@Component("music")
 public class Music {
     private String musicName = null;
 
     @Value("Dream")
     public void setMusicName(String musicName) {
         this.musicName = musicName;
     }
 
    public String appendMusicName(String appendValue) {
        return this.musicName + appendValue;
    }

    // 省略getter方法
}

则可这样调用ID为music的Bean的appendMusicName方法,把它的返回值注入player中:

@Component("player")
 public class Player {
     private String playingMusicName = null;
 
     @Value("#{music.appendMusicName(' work')}")
     public void setPlayingMusicName(String playingMusicName) {
         this.playingMusicName = playingMusicName;
     }
 
    // 省略getter方法
}

T()运算符

SpEL表达式提供了T()运算符用于获取类的Class对象。只要把全限定类名指给T()运算符,T()运算符即能获取指定类的Class对象。因此,假如com.dream包定义了这样的类:

public class MusicWorld {
    public static final String DREAM = "Dream";
}

则可这样通过SpEL表达式获取MusicWorld的Class对象,把它注入music中:

@Component("music")
 public class Music {
     private Class musicWorldClass = null;
 
     @Value("#{T(com.dream.MusicWorld)}")
     public void setMusicWorldClass(Class musicWorldClass) {
         this.musicWorldClass = musicWorldClass;
     }
 
    // 省略getter方法
}

一种常用的用法是使用T()运算符获取类的Class对象之后访问类的静态成员。如下所示:

@Component("music")
 public class Music {
     private String musicName = null;
     
     @Value("#{T(com.dream.MusicWorld).DREAM}")
     public void setMusicName(String musicName) {
         this.musicName = musicName;
     }
 
    // 省略getter方法
}

matches运算符

SpEL表达式支持通过matches运算符,以 字符串 matches 正则表达式 这样的方式判断某个字符串是否匹配某个正则表达式:如果匹配,则返回TRUE;否则返回FALSE。因此,假如com.dream包定义了这样的组件:

@Component("music")
 public class Music {
     private String musicName = null;
 
     @Value("Dream")
     public void setMusicName(String musicName) {
         this.musicName = musicName;
     }
 
    // 省略getter方法
}

则可这样在SpEL表达式里使用matches运算符,看看music的musicName属性的值是不是只有字母,并把运算结果注入player中:

@Component("player")
 public class Player {
     private boolean isMusicValid = false;
 
     @Value("#{music.musicName matches '^[a-zA-Z]+$'}")
     public void setIsMusicValid(boolean isMusicValid) {
         this.isMusicValid = isMusicValid;
     }
 
    // 省略getter方法
}

new运算符

SpEL表达式支持通过new运算符,以 new 带有全限定类名的构造函数 这样的方式创建类的实例。假如com.dream包定义了这样的类: 

public class Music {
    private String musicName = null;

    public Music(String musicName) {
        this.musicName = musicName;
    }
}

则可这样在SpEL表达式里使用new运算符创建Music类的实例,把实例注入player中:

@Component("player")
 public class Player {
     private Music playingMusic = null;
 
     @Value("#{new com.dream.Music('Dream')}")
     public void setMusic(Music playingMusic) {
         this.playingMusic = playingMusic;
     }
 
    // 省略getter方法
}

关系运行符

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的?:运算符是一样的。如下所示:

#{music.musicName != null ? music.musicName: 'There is not any music.'}

这条SpEL表达式会先检查 music.musicName 的值是否不等于null:如果不等于null,则返回 music.musicName 的值;否则返回字符串 There is not any music. 

另外,这条SpEL表达式还能使用Elvis运算符这样简化:

#{music.musicName ?: 'There is not any music.'}

Elvis运算符会先检查 music.musicName 的值是否不等于null:如果不等于null,则直接返回自己,也就是返回
 music.musicName 的值;否则返回 ?: 后面的值,也就是返回字符串 There is not any music. 

?.运算符

SpEL表达式提供了?.运算符用于安全地访问Bean的属性,调用Bean的方法。按照以往的写法,我们总是这样访问Bean的属性,调用Bean的方法:

#{music.musicName}
#{music.appendMusicName(' work')}

毫无疑问,如果music等于null,程序定会抛出异常。为了避免这样的错误,我们可用?.运算符修改如下:

#{music?.musicName}
#{music?.appendMusicName(' work')}

我们把 . 换成了?.运算符。这样,当music等于null时,这条SpEL表达式只会返回null,不会抛出异常;当music不等于null时,这条SpEL表达式才会访问Bean的属性,调用Bean的方法。

至此,关于SpEL表达式的介绍该告一段落了。当然,XML配置文件也是支持SpEL表达式的,而且用法与@Value注解一模一样。大家一看下面的示例就知道了:

<bean id="music_1" class="com.dream.Music">
    <property name="musicName" 
              value="#{T(com.dream.MusicWorld).DREAM + ' work'}" />
</bean>
<bean id="music_2" class="com.dream.Music">
    <constructor-arg type="java.lang.String" 
                     value="#{T(com.dream.MusicWorld).DREAM + ' work'}" />
</bean>
正文到此结束
评论插件初始化中...
Loading...