Spring3開發實戰 之 第八章:Spring3的表達式語言 – JAVA編程語言程序開發技術文章

是什麼
Spring表達式語言全稱為“Spring Expression Language”,縮寫為“SpEL”,類似於Struts2x中使用的OGNL表達式語言,能在運行時構建復雜表達式、存取對象圖屬性、對象方法調用等等,並能與Spring功能完美整合。
表達式語言給靜態Java語言增加瞭動態功能。
SpEL是單獨模塊,隻依賴於core模塊,不依賴於其他模塊,可單獨使用。
能幹什麼
表達式語言一般是用較為簡單的形式完成主要的工作,減少開發的工作量。
SpEL支持如下表達式:
一、基本表達式:字面量表達式、關系,邏輯與算數運算表達式、字符串連接及截取表達式、三目運算及Elivis表達式、正則表達式、括號優先級表達式;
二、類相關表達式:類類型表達式、類實例化、instanceof表達式、變量定義及引用、賦值表達式、自定義函數、對象屬性存取及安全導航表達式、對象方法調用、Bean引用;
三、集合相關表達式:內聯List、內聯數組、集合,字典訪問、列表,字典,數組修改、集合投影、集合選擇;不支持多維內聯數組初始化;不支持內聯字典定義;
四、其他表達式:模板表達式。
在Java中使用SpringEL是非常簡單的,示例如下:

java代碼:
查看復制到剪貼板打印
public class HelloWorld { 
public static void main(String[] args) { 
//構造上下文:準備比如變量定義等等表達式運行需要的上下文數據 
EvaluationContext context = new StandardEvaluationContext(); 
//創建解析器:提供SpelExpressionParser默認實現 
ExpressionParser parser = new SpelExpressionParser(); 
//解析表達式:使用ExpressionParser來解析表達式為相應的Expression對象 
Expression expression = 
parser.parseExpression("('Hello' + ' World').concat(#end)"); 
  
//設置上下文中的變量的值 
context.setVariable("end", "!SpringEL"); 
//執行表達式,獲取運行結果 
String str = (String)expression.getValue(context); 
System.out.println("the str="+str); 


ExpressionParser接口
表示解析器,默認實現是org.springframework.expression.spel.standard包中的SpelExpressionParser類,使用parseExpression方法將字符串表達式轉換為Expression對象。
配合 ExpressionParser 接口使用的ParserContext接口用於定義字符串表達式是不是模板,及模板開始與結束字符,示例如下:

java代碼:
查看復制到剪貼板打印
public static void main(String[] args) { 
 ExpressionParser parser = new SpelExpressionParser(); 
 //自定義一個解析模板的規則 
    ParserContext parserContext = new ParserContext() { 
        public boolean isTemplate() {             return true;        } 
        public String getExpressionPrefix() {     return "#{";        } 
        public String getExpressionSuffix() {     return "}";        } 
    }; 
    String template = "#{'Hello '}#{'World!'}"; 
    Expression expression = parser.parseExpression(template, parserContext); 
    System.out.println( expression.getValue()); 

說明
1:在此演示的是使用ParserContext的情況,此處定義瞭ParserContext實現:定義表達式是模塊,表達式前綴為“#{”,後綴為“}”;
2:使用parseExpression解析時傳入的模板必須以“#{”開頭,以“}”結尾,如“#{‘Hello ’}#{‘World!’}”。
3:請註意默認傳入的字符串表達式不是模板形式,如之前演示的Hello World。
 
Expression接口
表示表達式對象,默認實現是org.springframework.expression.spel.standard包中的SpelExpression,提供getValue方法用於獲取表達式值,提供setValue方法用於設置對象值
 
EvaluationContext接口
表示上下文環境,默認實現是org.springframework.expression.spel.support包中的StandardEvaluationContext類,使用setRootObject方法來設置根對象,使用setVariable方法來註冊自定義變量,使用registerFunction來註冊自定義函數等等。
 
字面量表達式:
 SpEL支持的字面量包括:字符串、數字類型(int、long、float、double)、佈爾類型、null類型 ,示例如下:

java代碼:
查看復制到剪貼板打印
1:String str1 = parser.parseExpression("'Hello World!'").getValue(String.class); 
2:String str2 = parser.parseExpression("\"Hello World!\"").getValue(String.class); 
3:int int1 = parser.parseExpression("1").getValue(Integer.class); 
4:float float1 = parser.parseExpression("1.1").getValue(Float.class); 
5:boolean true1 = parser.parseExpression("true").getValue(boolean.class); 
6:Object null1 = parser.parseExpression("null").getValue(Object.class); 
  
算數運算表達式 :
SpEL支持加(+)、減(-)、乘(*)、除(/)、求餘(%)、冪(^)運算,示例如下:
1:int result1 = parser.parseExpression("1+2-3*4/2").getValue(Integer.class);
2:int result2 = parser.parseExpression(“4%3”).getValue(Integer.class) ;
3:int result3 = parser.parseExpression("2^3").getValue(Integer.class);
SpEL還提供求餘(MOD)和除(DIV)運算符,與“%”和“/”等價,不區分大小寫。
關系表達式
等於(==)、不等於(!=)、大於(>)、大於等於(>=)、小於(<)、小於等於(<=),區間(between)運算,示例如下:
1:“parser.parseExpression(”1>2“).getValue(boolean.class);”將返回false;
2:“parser.parseExpression(”1 between {1, 2}“).getValue(boolean.class);”將返回true。
SpEL同樣提供瞭等價的“EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”來表示等於、不等於、大於、大於等於、小於、小於等於,不區分大小寫
 
邏輯表達式:且(and)、或(or)、非(!或NOT)。 示例如下:
1:String expression1 = "2>1 and (!true or !false)";
boolean result1 = parser.parseExpression(expression1).getValue(boolean.class);
註意:邏輯運算符不支持 Java中的 && 和 ||
 
字符串連接及截取表達式
使用“+”進行字符串連接,使用“‘String’ [index]”來獲取一個字符,目前隻支持獲取一個字符,如“'Hello ' + 'World!'”得到“Hello World!”;而“'Hello World!'[0]”將返回“H”
三目運算及Elivis運算表達式
1:三目運算符 “表達式1?表達式2:表達式3”用於構造三目運算表達式,如“2>1?true:false”將返回true;
2:Elivis運算符“表達式1?:表達式2”從Groovy語言引入,用於簡化三目運算符的,當表達式1為非null時則返回表達式1,當表達式1為null時則返回表達式2,如“null?:false”將返回false,而“true?:false”將返回true;
 
正則表達式
使用“str matches regex,如“‘123’ matches ‘\\d{3}’”將返回true;
 
括號優先級表達式
使用“(表達式)”構造,括號裡的具有高優先級。
類類型表達式
使用“T(Type)”來表示java.lang.Class實例,“Type”必須是類全限定名,“java.lang”包除外,即該包下的類可以不指定包名;使用類類型表達式還可以進行訪問類靜態方法及類靜態字段。 示例如下:
1:訪問java.lang包的類
Class<String> result1 = parser.parseExpression("T(String)").getValue(Class.class);
2:訪問其他包下的類 :
String expression2 = "T(cn.javass.spring.chapter5.SpELTest)";
    Class<String> result2 = parser.parseExpression(expression2).getValue(Class.class);
3:訪問類的靜態字段
int result3=parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class);
4:訪問類的靜態方法
int result4 = parser.parseExpression("T(Integer).parseInt('1')").getValue(int.class);
類實例化
類實例化同樣使用java關鍵字“new”,類名必須是全限定名,但java.lang包內的類型除外,如String、Integer。示例如下:
1:String result1 = parser.parseExpression("new String('hello')").getValue(String.class);
2:Date result2 = parser.parseExpression("new java.util.Date()").getValue(Date.class);
 
instanceof表達式
SpEL支持instanceof運算符,跟Java內使用同義;如“hello‘ instanceof T(String)”將返回true。
 
變量定義及引用
1:變量通過EvaluationContext接口的setVariable(variableName, value)方法定義
2:在表達式中使用“#variableName”引用;
3:除瞭引用自定義變量,SpE還允許引用根對象及當前上下文對象,使用“#root”引用根對象,使用“#this”引用當前上下文對象;
示例如下:

java代碼:
查看復制到剪貼板打印
ExpressionParser parser = new SpelExpressionParser(); 
EvaluationContext context = new StandardEvaluationContext(); 
context.setVariable("variable", "hello1"); 
context.setVariable("variable", "hello2"); 
String result1 = parser.parseExpression("#variable").getValue(context, String.class); 
System.out.println("r1=="+result1); 
  
context = new StandardEvaluationContext(12); 
String result2 = parser.parseExpression("#root-1").getValue(context, String.class); 
System.out.println("r2=="+result2); 
String result3 = parser.parseExpression("#this").getValue(context, String.class); 
System.out.println("r3=="+result3); 
  
輸出結果:
r1==hello2
r2==11
r3==12
自定義函數
目前隻支持類靜態方法註冊為自定義函數;SpEL使用StandardEvaluationContext的registerFunction方法進行註冊自定義函數,其實完全可以使用setVariable代替,兩者其本質是一樣的。示例如下:

java代碼:
查看復制到剪貼板打印
ExpressionParser parser = new SpelExpressionParser(); 
StandardEvaluationContext context = new StandardEvaluationContext(); 
Method parseInt = 
       Integer.class.getDeclaredMethod("parseInt", String.class); 
context.registerFunction("regParseInt", parseInt); 
context.setVariable("parseInt2", parseInt); 
String expression1 = "#regParseInt('3') == #parseInt2('3')"; 
boolean result = 
    parser.parseExpression(expression1).getValue(context, boolean.class); 
System.out.println("result="+result); 
可以看出“registerFunction”和“setVariable”都可以註冊自定義函數,但是兩個方法的含義不一樣,推薦使用“registerFunction”方法註冊自定義函數。
賦值表達式
SpEL即允許給自定義變量賦值,也允許給跟對象賦值,直接使用“#variableName=value”即可賦值。示例如下:

java代碼:
查看復制到剪貼板打印
1:parser.parseExpression("#root=‘Hi'").getValue(context, String.class); 
2:parser.parseExpression("#this=‘Hi'").getValue(context, String.class); 
3:context.setVariable("#variable", "variable"); 
  
對象屬性存取及安全導航表達式
對象屬性獲取非常簡單,使用如“a.property.property”這種點綴式獲取,SpEL對於屬性名首字母是不區分大小寫的;
SpEL還引入瞭Groovy語言中的安全導航運算符“(對象|屬性)?.屬性”,用來避免當“?.”前邊的表達式為null時拋出空指針異常,而是返回null;
修改屬性值可以通過賦值表達式或Expression接口的setValue方法修改。
示例如下:

java代碼:
查看復制到剪貼板打印
UserModel um = new UserModel(); 
um.setUuid("User1"); 
um.setName("UserName1"); 
ExpressionParser parser = new SpelExpressionParser(); 
StandardEvaluationContext context = new StandardEvaluationContext(); 
context.setVariable("um",um); 
//取值 
Expression expression = parser.parseExpression("'uuid='+#um.uuid + ',name='+#um.name"); 
String v =  expression.getValue(context,String.class); 
System.out.println("v=="+v); 
//賦值 
expression = parser.parseExpression("'uuid='+(#um.uuid='newUser') + ',name='+#um.name"); 
v =  expression.getValue(context,String.class); 
System.out.println("v2=="+v); 
輸出結果: 
v==uuid=User1,name=UserName1 
v2==uuid=newUser,name=UserName1 
對象方法調用
對象方法調用更簡單,跟Java語法一樣;如“‘Hello’.substring(1,3)”將返回“el”;而對於根對象可以直接調用方法。
Bean引用
SpEL支持使用“@”符號來引用Bean,在引用Bean時需要使用BeanResolver接口實現來查找Bean,Spring提供BeanFactoryResolver實現,示例如下:

java代碼:
查看復制到剪貼板打印
ApplicationContext ctx = new ClassPathXmlApplicationContext( 
        new String[] {"applicationContext.xml"}); 
  
ExpressionParser parser = new SpelExpressionParser(); 
StandardEvaluationContext context = new StandardEvaluationContext(); 
context.setBeanResolver(new BeanFactoryResolver(ctx)); 
String result1 = 
parser.parseExpression("@myBean.test()").getValue(context, String.class); 
內聯List
從Spring3.0.4開始支持內聯List,使用{表達式,……}定義內聯List,如“{1,2,3}”將返回一個整型的ArrayList,而“{}”將返回空的List,對於字面量表達式列表,SpEL會使用java.util.Collections.unmodifiableList方法將列表設置為不可修改。示例如下:
1://將返回不可修改的空List
List<Integer> result2 = parser.parseExpression("{}").getValue(List.class);
2:對於字面量列表也將返回不可修改的List
ExpressionParser parser = new SpelExpressionParser();
List<Integer> result1 = parser.parseExpression("{1,2,3}").getValue(List. class);
//result1.set(0, 2);//這句話會報錯,因為list不可以修改
System. out.println(result1);
3://對於列表中隻要有一個不是字面量表達式,將隻返回原始List,
//不會進行不可修改處理,也就是可以修改
String expression3 = "{{1+2,2+4},{3,4+4}}";
List<List<Integer>> result3 = parser.parseExpression(expression3).getValue(List.class);
result3.get(0).set(0, 1);
內聯數組
和Java 數組定義類似,隻是在定義時進行多維數組初始化。 示例如下:
1://定義一維數組並初始化
int[] result1 = parser.parseExpression("new int[1]").getValue(int[].class);
2://聲明二維數組並初始化
int[] result2 = parser.parseExpression("new int[2]{1,2}").getValue(int[].class);
3://定義多維數組但不初始化
int[][][] result3 = parser.parseExpression(expression3).getValue(int[][][].class);
4://錯誤的定義多維數組,多維數組不能初始化
String expression4 = "new int[1][2][3]{{1}{2}{3}}";
int[][][] result4 = parser.parseExpression(expression4).getValue(int[][][].class);
訪問元素
SpEL目前支持所有集合類型和字典類型的元素訪問,使用“集合[索引]”訪問集合元素,使用“map[key]”訪問字典元素 。示例如下;
1://SpEL內聯List訪問
int result1 = parser.parseExpression("{1,2,3}[0]").getValue(int.class);
//相當於result1.get(0)
 
2://SpEL目前支持所有集合類型的訪問
Collection<Integer> collection = new HashSet<Integer>();
collection.add(1);
collection.add(2);
EvaluationContext context2 = new StandardEvaluationContext();
context2.setVariable("collection", collection);
int result2 = parser.parseExpression("#collection[1]").getValue(context2, int.class);
3://SpEL對Map字典元素訪問的支持
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
EvaluationContext context3 = new StandardEvaluationContext();
context3.setVariable("map", map);
int result3 = parser.parseExpression("#map['a']").getValue(context3, int.class);
 
元素修改
可以使用賦值表達式或Expression接口的setValue方法修改。示例如下:
1://修改數組元素值
int[] array = new int[] {1, 2};
EvaluationContext context1 = new StandardEvaluationContext();
context1.setVariable("array", array);
int result1 = parser.parseExpression("#array[1] = 3").getValue(context1, int.class);
2://修改集合值
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(1);
collection.add(2);
EvaluationContext context2 = new StandardEvaluationContext();
context2.setVariable("collection", collection);
int result2 = parser.parseExpression("#collection[1] = 3").getValue(context2, int.class);
parser.parseExpression("#collection[1]").setValue(context2, 4);
result2 = parser.parseExpression("#collection[1]").getValue(context2, int.class);
3://修改map元素值
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
EvaluationContext context3 = new StandardEvaluationContext();
context3.setVariable("map", map);
int result3 = parser.parseExpression("#map['a'] = 2").getValue(context3, int.class);
集合投影
在SQL中投影指從表中選擇出列,而在SpEL指從集合中的元素,通過選擇來構造新的集合,該集合和原集合具有相同數量的元素,但可能屬性不一樣;SpEL使用“(list|map).![投影表達式]”來進行投影運算。
1:示例集合的投影
//1.首先準備測試數據
Collection<UserModel> collection = new ArrayList<UserModel>();
UserModel um1 = new UserModel();
um1.setUuid("u1");
um1.setName("u1Name");
collection.add(um1);
UserModel um2 = new UserModel();
um2.setUuid("u2");
um2.setName("u2Name");
collection.add(um2);
//2.測試集合或數組
EvaluationContext context1 = new StandardEvaluationContext();
ExpressionParser parser = new SpelExpressionParser();
context1.setVariable("collection", collection);
Collection<String> result1 =
parser.parseExpression(" #collection.![#this.name]").getValue(context1, Collection.class);
SpEL投影運算還支持Map投影,但Map投影最終隻能得到List結果,對於投影表達式中的“#this”將是Map.Entry,所以可以使用“value”來獲取值,使用“key”來獲取鍵。 示例如下:
//1.首先準備測試數據

java代碼:
查看復制到剪貼板打印
UserModel um1 = new UserModel(); 
um1.setUuid("u1"); 
um1.setName("u1Name"); 
UserModel um2 = new UserModel(); 
um2.setUuid("u2"); 
um2.setName("u2Name"); 
Map<String, UserModel> map = new HashMap<String, UserModel>(); 
map.put(um1.getUuid(),um1);    
map.put(um2.getUuid(),um2); 
//2.測試Map投影 
EvaluationContext context1 = new StandardEvaluationContext(); 
ExpressionParser parser = new SpelExpressionParser(); 
context1.setVariable("map", map); 
Collection<String> result1 = 
parser.parseExpression("#map.![#this.value.name]").getValue(context1, Collection.class); 

集合篩選
在SpEL指根據原集合通過條件表達式選擇出滿足條件的元素並構造為新的集合,SpEL使用“(list|map).?[選擇表達式]”,其中選擇表達式結果必須是boolean類型,如果true則選擇的元素將添加到新集合中,false將不添加到新集合中。示例如下:
//1:準備測試數據的過程跟上一個示例一樣,就不重復瞭
//2.測試集合或數組的篩選

java代碼:
查看復制到剪貼板打印
EvaluationContext context1 = new StandardEvaluationContext(); 
ExpressionParser parser = new SpelExpressionParser(); 
context1.setVariable("collection", collection); 
Collection<String> result1 = 
parser.parseExpression("#collection.?[#this.uuid.equals('u1')]").getValue(context1, Collection.class); 
  
//測試Map篩選

java代碼:
查看復制到剪貼板打印
EvaluationContext context1 = new StandardEvaluationContext(); 
ExpressionParser parser = new SpelExpressionParser(); 
context1.setVariable("map", map); 
Map<String,UserModel> result1 = 
parser.parseExpression("#map.?[#this.key=='u1']").getValue(context1, Map.class); 
nXml風格的配置
SpEL支持在Bean定義時使用,默認使用“#{SpEL表達式}”表示,不允許嵌套。其中“#root”根對象默認可以認為是ApplicationContext,獲取根對象屬性其實是獲取容器中的Bean。
nXml風格的配置示例—-通過SpEL表達式設置值

java代碼:
查看復制到剪貼板打印
<bean id="numberGuess" class="org.spring.samples.NumberGuess"> 
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> 
</bean> 
Xml風格的配置示例—-通過SpEL表達式參照其他的Bean

java代碼:
查看復制到剪貼板打印
<bean id="t1" class="cn.javass.spring3.hello.T2"> 
    <property name="value" value="#{ T(java.lang.Math).random() * 100.0 }"/> 
</bean> 
<bean id="t2" class="cn.javass.spring3.hello.T2"> 
    <property name="value" value="#{ T(Double).parseDouble(t1.value) -1 }"></property> 
</bean> 
上面的t1就會被解析成為參照t1這個Bean,當然也可以使用@t1來表示。
註解風格的配置
使用@Value註解來指定SpEL表達式,該註解可以放到字段、方法及方法參數上。 但是要在配置文件中使用<context:annotation-config/> 來開啟對註解的支持。示例如下:

java代碼:
查看復制到剪貼板打印
public class SpELBean { 
    @Value("#{ T(java.lang.Math).random() * 100.0 }") 
    private String value; 
    //setter和getter由於篇幅省略,自己寫上 

作者:jinnianshilongnian

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。