Spring學習總結(四)——表示式語言 Spring Expression Language
Spring表示式語言(簡稱SpEL)是一個支援查詢並在執行時操縱一個物件圖的功能強大的表示式語言。SpEL語言的語法類似於統一EL,但提供了更多的功能,最主要的是顯式方法呼叫和基本字串模板函式。
同很多可用的Java 表示式語言相比,例如OGNL,MVEL和JBoss EL,SpEL的誕生是為了給Spring社群提供一個可以給Spring目錄中所有產品提供單一良好支援的表示式語言。其語言特性由Spring目錄中的專案需求驅動,包括基於eclipse的SpringSource套件中的程式碼補全工具需求。也就是說,SpEL是一個基於技術中立的API允許需要時與其他表示式語言整合。
SpEL作為Spring目錄中表達式求值的基礎,它並不是直接依賴於Spring而是可以被獨立使用。為了能夠自包含,本章中的許多示例把SpEL作為一個獨立的表示式語言來使用。這就需要建立一些如解析器的引導基礎元件類。大多數Spring使用者只需要為求值編寫表示式字串而不需要關心這些基礎元件
SpEL功能特性:
- 字元表示式
- 布林和關係操作符
- 正則表示式
- 類表示式
- 訪問properties,arrays,lists,maps
- 方法呼叫
- 關係操作符
- 賦值
- 呼叫構造器
- 三元操作符
- 變數
- 使用者自定義函式
- 集合投影
- 集合選擇
- 模板表示式
一、為什麼需要Spring表示式語言
1.1、新建一個Maven Web專案,新增依賴,pom.xml如下所示:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>Spring053</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Spring053</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> <version>4.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.4</version> </dependency> </dependencies> </project>
表示式語言所依賴的包如下所示:
2.2、為了IOC,定義了使用者類User.java與Order.java,如下所示:
使用者類:
package com.zhangguo.Spring053.spel01; /** * 訂單類 * */ public class Order { /** * 訂單名稱 */ private String orderName; /* * 使用者姓名 */ private String userName; /** * 使用者物件 */ private User customer; public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public User getCustomer() { return customer; } public void setCustomer(User customer) { this.customer = customer; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "訂單名:"+this.getOrderName()+",姓名:"+this.getUserName()+",編號:"+this.getCustomer().getId(); } }View Code
訂單類:
package com.zhangguo.Spring053.spel01;
/**
* 訂單類
*
*/
public class Order {
/**
* 訂單名稱
*/
private String orderName;
/*
* 使用者姓名
*/
private String userName;
/**
* 使用者物件
*/
private User customer;
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public User getCustomer() {
return customer;
}
public void setCustomer(User customer) {
this.customer = customer;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
View Code
2.3、編寫容器初始化的配置檔案spel01.xml,內容如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <bean id="gyl" class="com.zhangguo.Spring053.spel01.User" p:id="9527"> <property name="name" value="郭永樂"> </property> </bean> <bean id="order001" class="com.zhangguo.Spring053.spel01.Order"> <property name="customer" ref="gyl"></property> <property name="name" value="#{gyl.name}"></property> <property name="orderName" value='#{"Apples".toUpperCase()}'></property> </bean> </beans>
在配置檔案中,出現了#{}形式的表示式,我們就稱為Spel表示式。#{gyl.name}作用是找到名稱為gyl的bean取出中間的name值;#{"Apples".toUpperCase()}把字串Apples轉換成大寫並輸出。
2.4、取出bean測試
package com.zhangguo.Spring053.spel01; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("spel01.xml"); Order order=ctx.getBean("order001",Order.class); System.out.println(order); } }
2.5、執行結果
二、SpEL表示式Hello World!
Spring表示式語言(SpEL)從3.X開始支援,它是一種能夠支援執行時查詢和操作物件圖的強大的表示式,其表示式語法類似於統一表達式語言。
SpEL支援如下表達式:
基本表示式:字面量表達式、關係,邏輯與算數運算表示式、字串連線及擷取表示式、三目運算、正則表示式、括號優先順序表示式;
類相關表示式:類型別表示式、類例項化、instanceof表示式、變數定義及引用、賦值表示式、自定義函式、物件屬性存取及安全導航表示式、物件方法呼叫、Bean引用;
集合相關表示式:內聯List、內聯陣列、集合,字典訪問、列表,字典,陣列修改、集合投影、集合選擇;不支援多維內聯陣列初始化;不支援內聯字典定義;
其他表示式:模板表示式。
從一個Hello World!的示例開始:
package com.zhangguo.Spring053.spel02; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class Test { public static void main(String[] args) { //建立SpEL表示式的解析器 ExpressionParser parser=new SpelExpressionParser(); //解析表示式'Hello '+' World!' Expression exp=parser.parseExpression("'Hello '+' World!'"); //取出解析結果 String result=exp.getValue().toString(); //輸出結果 System.out.println(result); } }
執行結果:
Hello World!
從示例中可以看出java成功的將一個字元解析出了結果,如果我們把要解析的內容設定成1+1則會解析出2來,這裡的1+1就是一個SpEL表示式,該表示式在java中只是一個String,通過解析可以得到字串本身的意見,有點型別編譯程式的感覺。
三、SpEL表示式
3.1、文字表達式
支援的文字表達的型別是字串,日期,數值(整型,實型,和十六進位制),布林和空。字串是由單引號分隔。使用反斜槓字元轉移把一個單引號字元本身放在字串中。
ExpressionParser ep= new SpelExpressionParser(); System.out.println(ep.parseExpression("'HelloWorld'").getValue()); System.out.println(ep.parseExpression("0xffffff").getValue()); System.out.println(ep.parseExpression("1.234345e+3").getValue()); System.out.println(ep.parseExpression("new java.util.Date()").getValue());
執行結果:
HelloWorld
16777215
1234.345
Fri Jul 01 14:50:59 CST 2016
3.2、SPEL語言特性
3.2.1、屬性
//建立SpEL表示式的解析器 ExpressionParser parser=new SpelExpressionParser(); User user=new User(9527,"周星馳"); //解析表示式需要的上下文,解析時有一個預設的上下文 EvaluationContext ctx = new StandardEvaluationContext(); //在上下文中設定變數,變數名為user,內容為user物件 ctx.setVariable("user", user); //從使用者物件中獲得id並+1900,獲得解析後的值在ctx上下文中 int id = (Integer) parser.parseExpression("#user.getId() + 1900").getValue(ctx); System.out.println(id);
執行結果:
11427
User類在前面已定義,這裡增加了一個有參構造方法。上面的示例是呼叫方法,其實可以這樣:引用物件屬性,只需使用一個句點來表示一個巢狀的屬性值,如下程式碼所示:
int id = (Integer) parser.parseExpression("#user.id + 1900").getValue(ctx);
執行結果:
11427
要注意的是此時#user後不再是一個方法而是.id,直接訪問屬性,在java中這樣做是不行的,便SpEL中允許
3.2.2、陣列
String[] students=new String[]{"tom","jack","rose","mark","lucy"}; ctx.setVariable("students", students); String student = parser.parseExpression("#students[3]").getValue(ctx, String.class); System.out.println(student);
結果:mark
3.2.3、列表
List numbers = (List) parser.parseExpression("{1,2,3,4,5}").getValue(); System.out.println(numbers.get(2)+""); List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(); System.out.println(((List)listOfLists.get(1)).get(1));
結果:3 y
3.2.4、索引器、與字典
User user1=new User(9001,"鄒任飛"); User user2=new User(9002,"練汶峰"); List<User> users=new ArrayList<User>(); users.add(user1); users.add(user2); ctx.setVariable("users", users); String name = parser.parseExpression("#users[1].name").getValue(ctx,String.class); System.out.println(name);
結果:練汶峰
在java中訪問集合中的物件通過get(索引)而在SpEL中我們可以直接像陣列那樣訪問物件。如果是一個字典可以這樣:#users["tom"].id
3.2.5、方法
方法呼叫使用典型的Java程式設計語法。 你可能 還在文字呼叫方法。 也支援可變引數。
String c = parser.parseExpression("'abcdef'.substring(2, 3)").getValue(String.class); System.out.println(c);
結果:c
3.2.6、操作符
關係操作符:使用標準的操作符號支援關係操作符:等於,不等於,小於,小於等於,大於,大於等於。
邏輯操作符:支援的邏輯操作符包括and,or和not(!),不支援&&和||。
算術操作符:加法運算子可以用於數字,字串和日期。減法可用於數字和日期。乘法和除法僅可以用於。其他支援的數學運算包括取模(%)和指數冪(^)。使用標準的運算子優先順序。
關係運算符:
//true boolean trueValue1 = parser.parseExpression("2 == 2").getValue(Boolean.class); //false boolean falseValue1 = parser.parseExpression("2 < -5.0").getValue(Boolean.class); //true boolean trueValue2 = parser.parseExpression("'black' < 'block'").getValue(Boolean.class); //false,字元xyz是否為int型別 boolean falseValue2 = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class); //true,正則是否匹配 boolean trueValue3 =parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); //false boolean falseValue3=parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
邏輯運算:
// -- AND 與運算 -- //false boolean falseValue4 = parser.parseExpression("true and false").getValue(Boolean.class); //true,isMember方法用於測試是否為某個物件的成員 String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"; boolean trueValue4 = parser.parseExpression(expression).getValue(Boolean.class); // -- OR 或運算-- //true boolean trueValue5 = parser.parseExpression("true or false").getValue(Boolean.class); //true String expression1 = "isMember('Nikola Tesla') or isMember('Albert Einstein')"; boolean trueValue6 = parser.parseExpression(expression).getValue( Boolean.class); //false boolean falseValue5 = parser.parseExpression("!true").getValue(Boolean.class); //false String expression2 = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; boolean falseValue6 = parser.parseExpression(expression).getValue(Boolean.class);
算術運算:
// Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string' // Subtraction int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 // Multiplication int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 // Division int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 // Modulus int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 // Operator precedence int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
3.2.7、表示式支援定義bean、基於XML的配置
在xml配置中可以自由的使用SpEL,如下所示:
<bean id="gyl" class="com.zhangguo.Spring053.spel01.User" p:id="9527"> <property name="name" value="郭永樂"> </property> </bean> <bean id="order001" class="com.zhangguo.Spring053.spel01.Order"> <property name="customer" ref="gyl"></property> <property name="userName" value="#{gyl.name}"></property> <property name="orderName" value='#{"Apples".toUpperCase()}'></property> </bean> <bean id="numberGuess" class="org.spring.samples.NumberGuess"> <!--呼叫靜態方法random() --> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }" /> </bean> <bean id="taxCalculator" class="org.spring.samples.TaxCalculator"> <property name="defaultLocale" value="#{ systemProperties['user.region'] }" /> </bean> <bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }" /> </bean> <bean id="shapeGuess" class="org.spring.samples.ShapeGuess"> <property name="initialShapeSeed" value="#{ numberGuess.randomNumber }" /> </bean>
3.2.8、表示式支援定義bean、基於註解的配置
使用者類User.java:
package com.zhangguo.Spring053.spel03; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * 使用者類 */ @Component("user1") public class User { /** * 編號 */ @Value("#{9527+100}") private int id; /** * 姓名 */ @Value("#{'Hello'.toUpperCase()}") private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
訂單類:Order.java
package com.zhangguo.Spring053.spel03; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; /** * 訂單類 * */ @Repository("Order03") public class Order { /** * 訂單名稱 */ @Value("#{'Apple訂單'}") private String orderName; /* * 使用者姓名 */ @Value("#{user1.name}") private String userName; /** * 使用者物件 */ @Value("#{user1}") private User customer; public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public User getCustomer() { return customer; } public void setCustomer(User customer) { this.customer = customer; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "訂單名:"+this.getOrderName()+",姓名:"+this.getUserName()+",編號:"+this.getCustomer().getId(); } }
測試程式碼Test.java
package com.zhangguo.Spring053.spel03; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zhangguo.Spring053.spel03.User; public class Test { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("spel03.xml"); User user1=ctx.getBean("user1",User.class); System.out.println(user1.getId()+","+user1.getName()); Order Order03=ctx.getBean("Order03",Order.class); System.out.println(Order03); } }
執行結果:
9627,HELLO
訂單名:Apple訂單,姓名:HELLO,編號:9627
3.2.9、操作符
ExpressionParser ep = new SpelExpressionParser(); // 關係操作符 System.out.println(ep.parseExpression("5>2").getValue()); System.out.println(ep.parseExpression("2 between {1,9}").getValue()); // 邏輯運算子 System.out.println(ep.parseExpression("(5>2) and (2==1)").getValue()); // 算術操作符 System.out.println(ep.parseExpression("100-2^2").getValue());
結果:
true
true
false
96
3.2.10、變數與賦值
變數:變數可以在表示式中使用語法#’變數名’引用
ExpressionParser ep= new SpelExpressionParser();
//建立上下文變數
EvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable(“name”, “Hello”);
System.out.println(ep.parseExpression("#name").getValue(ctx));
輸出:Hello
賦值:屬性設定是通過使用賦值運算子。這通常是在呼叫setValue中執行但也可以在呼叫getValue內,也可通過”#varName=value”的形式給變數賦值。
System.out.println(ep.parseExpression("#name='Ryo'").getValue(ctx));
輸出:Ryo