SpEL表達式總結


原文地址:https://blog.csdn.net/u010086122/article/details/81566515

前言
        SpEL(Spring Expression Language),即Spring表達式語言。它是一種類似JSP的EL表達式、但又比后者更為強大有用的表達式語言。

        為什么要用SpEL:因為它可以在spring容器內實時查詢和操作數據,尤其是操作List列表型、Array數組型數據。所以使用SpEL可以有效縮減代碼量,優化代碼結構,筆者認為很有用。

一. 用法
常規SpEL有三種用法:

在注解@Value中使用
在XML配置中使用
在代碼中創建Expression對象,利用Expression對象來執行SpEL
1. @Value注解
@Value可以加在class的成員變量和形參上。用法如下

//@Value能修飾成員變量和方法形參
    //#{}內就是SpEL表達式的語法
    //Spring會根據SpEL表達式語法,為變量arg賦值
    @Value("#{表達式}")
    public String arg;

如果修飾成員變量,Spring容器會根據SpEL表達式語法篩選修改數據,然后賦值給所@Value修飾的變量;

如果修飾方法形參,則是過濾傳進來的參數值。

2. XML配置
XML配置用在Spring的applicationContext.xml配置文件內的<bean>元素上,用法如下:

<bean id="xxx" class="com.java.XXXXX.xx">
    <!-- 同@Value,#{}內是表達式的值,可放在property或constructor-arg內 -->
    <property name="arg" value="#{表達式}">
</bean>
 
  •       用法跟注解@Value修飾形參類似  

3. Expression​​​​​​ 

在代碼中創建Expression對象,利用Expression對象來執行SpEL

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
 
public class SpELTest {
 
    public static void main(String[] args) {
 
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL表達式語法設置在parseExpression()入參內
        Expression exp = parser.parseExpression("表達式");
        //執行SpEL表達式,執行的默認Spring容器是Spring本身的容器:ApplicationContext
        Object value = exp.getValue();
        
 
        /**也可以使用非Spring的ApplicationContext容器,則用下面的方法*/
        //創建一個虛擬的容器EvaluationContext
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //向容器內添加bean
        BeanA beanA = new BeanA();
        ctx.setVariable("bean_id", beanA);
        //setRootObject並非必須;一個EvaluationContext只能有一個RootObject,引用它的屬性時,可以不加前綴
        ctx.setRootObject(XXX);        
        //getValue有參數ctx,從新的容器中根據SpEL表達式獲取所需的值
        Object value = exp.getValue(ctx);
    }
}
 

   Expression用法可以在代碼中使用SpEL進行數據的讀取過濾和修改,十分方便。

4. 總結
        由上面可以看出,SpEL與Spring容器本身緊密相關,且用法特別靈活,可以直接操作Spring管理的各種bean、變量、properties配置文件等數據。

     以上概念還可以參考:SpEL詳解

 
二. SpEL表達式語法
        知道SpEL的使用場景范圍和用法后,我們來看下SpEL表達式的具體語法。

        SpEL語法決定了程序員能取到通過編寫SpEL,從容器內取到什么樣的數據,以及可以把數據處理成什么結果。有時用java代碼寫很多行的邏輯,用SpEL表達式一句話就可以搞定。

具體如下:

1.  直接量表達式

@Value("#{'Hello World'}")        
    String word;        //變量word賦值直接量:字符串"Hello World"

2.  直接使用java代碼

如在SpEL中直接試用new/instance of,像寫Java代碼一樣。注意:在SpEL中直接使用某個類名時,此類必須是java.lang 包中的類,才可以在SpEL中省略包名;否則需要寫全名

 Expression exp = parser.parseExpression("new Spring('Hello World')");

3.  使用T(Type)

        使用“T(Type)”來表示java.lang.Class類的實例,即如同java代碼中直接寫類名。同樣,只有java.lang 下的類才可以省略包名。此方法一般用來引用常量或靜態方法

parser.parseExpression("T(Integer).MAX_VALUE");    //等同於java代碼中的:Integer.MAX_VALUE

4.  變量

        獲取容器內的變量,可以使用“#bean_id”來獲取。有兩個特殊的變量,可以直接使用。

  •     #this 使用當前正在計算的上下文
  •     #root 引用容器的root對象,即applicationContext本身
        //從ctx容器內,獲取rootObject,並轉換為String類型
        String result1 = parser.parseExpression("#root").getValue(ctx, String.class);  
        
        //在ctx容器內,設置abc的值為"abcdef"
        String s = new String("abcdef");
        ctx.setVariable("abc",s);
        //取id為abc的bean,然后調用其中的substring方法,得到結果賦值給result2
        String result2 = parser.parseExpression("#abc.substring(0,1)").getValue(ctx, String.class); 

5.  方法調用

    SpEL的方法調用與直接編寫Java代碼沒有什么區別。具體可見上例abc.substring(0,1)與java代碼"abcdef".substring(0,1)效果一樣

    SpEL也可以自定義方法,如下:

        //創建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //獲取java自帶的Integer類的parseInt(String)方法
        Method parseInt = Integer.class.getDeclaredMethod("parseInt", String.class);
        //將parseInt方法注冊在ctx容器內
        ctx.registerFunction("parseInt", parseInt);
        //再將parseInt方法設為parseInt2
        ctx.setVariable("parseInt2", parseInt);
 
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL語法,比對兩個方法執行完成后,結果是否相同
        String expreString = "#parseInt('2') == #parseInt2('3')";
        Expression expression = parser.parseExpression(expreString);
        return expression.getValue(ctx, Boolean.class);    //執行結果為false
 
        /** 如果String expreString = "#parseInt('2') == #parseInt2('3')",執行結果為true */
        /** 可見SpEL正常執行*/

  “registerFunction”和“setVariable”都可以注冊自定義函數,但是兩個方法的含義不一樣,推薦使用“registerFunction”方法注冊自定義函數。

6.  運算符表達式
算數表達式(“1+2-3*4/2″)
比較表達式(“1>2”)
邏輯表達式(“2>1 and (!true or !false)”)
賦值表達式(“#variableName=value”)
三目表達式(“表達式1?表達式2:表達式3”)
正則表達式(“123′ matches ‘\\d{3}”)
            等運算符,都可以直接放在SpEL中,執行結果為運算符表達式的結果

7.  Elvis運算符
        是三目運算符的特殊寫法,可以避免null報錯的情況

    //SpEL可簡寫為:
    name?:"other"
 
    //等同於java代碼
    name != null? name : "other"

8.  安全保證

        為了避免操作對象本身可能為null,取屬性時報錯,可以使用SpEL檢驗語法

        語法: “對象?.變量|方法”

    //SpEL表達式簡寫
    list?.length
 
    //等同於java代碼
    list == null? null: list.length
 

  當對象為null時,直接返回“null”,不會拋出NullPointerException

9.   集合定義

        使用“{表達式,……}”定義List,如“{1,2,3}”

    //SpEL的@Value注解設置List
    @Value("1,2,3")
    private List<Integer> f1;
 
    @RequestMapping(value = "/a", method = RequestMethod.POST)
    public List<Integer> a() throws NoSuchMethodException {
 
        //SpEL
        List<Integer> result1 = parser.parseExpression("{1,2,3}").getValue(List.class);
 
        //等同於如下java代碼
        Integer[] integer = new Integer[]{1,2,3};
        List<Integer> result2 = Arrays.asList(integer);
 
        return result1;
    }

  對於字面量表達式列表,SpEL會使用java.util.Collections.unmodifiableList 方法將列表設置為不可修改。

10.  集合訪問
        SpEL目前支持所有集合類型和字典類型的元素訪問

        語法:“集合[索引]”、“map[key]”

EvaluationContext context = new StandardEvaluationContext();
 
//即list.get(0)
int result1 = parser.parseExpression("{1,2,3}[0]").getValue(int.class); 
 
//list獲取某一項
Collection<Integer> collection = new HashSet<Integer>();
collection.add(1);
collection.add(2);
 
context.setVariable("collection", collection);
int result2 = parser.parseExpression("#collection[1]").getValue(context, int.class); 
 
//map獲取
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
 
context.setVariable("map", map);
int result3 = parser.parseExpression("#map['a']").getValue(context, int.class); 

11.  集合修改

可以使用賦值表達式或Expression接口的setValue方法修改;

//賦值語句
int result = parser.parseExpression("#array[1] = 3").getValue(context, int.class); 
 
//serValue方法
parser.parseExpression("#array[2]").setValue(context, 4);

12.  集合選擇
        通過一定的規則對及格進行篩選,構造出另一個集合

        語法:“(list|map).?[選擇表達式]”

            選擇表達式結果必須是boolean類型,如果true則選擇的元素將添加到新集合中,false將不添加到新集合中。

    parser.parseExpression("#collection.?[#this>2]").getValue(context, Collection.class); 

  上面的例子從數字的collection集合中選出數字大於2的值,重新組裝成了一個新的集合

13.  集合投影
        根據集合中的元素中通過選擇來構造另一個集合,該集合和原集合具有相同數量的元素

        語法:“SpEL使用“(list|map).![投影表達式]”

 

public class Book {
 
    public String name;         //書名
    public String author;       //作者
    public String publisher;    //出版社
    public double price;        //售價
    public boolean favorite;    //是否喜歡
}
public class BookList {
 
    @Autowired
    protected ArrayList<Book> list = new ArrayList<Book>() ;
    
    protected int num = 0;
}

將BookList的實例映射為bean:readList,在另一個bean中注入時,進行投影

    //從readList的list下篩選出favorite為true的子集合,再將他們的name字段投為新的list
    @Value("#{list.?[favorite eq true].![name]}")
    private ArrayList<String> favoriteBookName;

 如果想了解更多SpEL語法,也可以參考:SpEL語法​​​


 

附贈測試案例 

 
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 *
 */
@Slf4j
@RestController
@RequestMapping("/SpEL")
public class SpELController {
 
    @Value("#{'Hello World1'}")
    String helloWorld1;        //變量word賦值直接量:字符串"Hello World"
 
    @Value("Hello World2")
    String helloWorld2;        //變量word賦值直接量:字符串"Hello World"
 
    //注入list
    @Value("7,2,3,5,1")
    private List<Integer> fList;
 
 
    /**
     * {@code @Value} 注入String
     *
     * @return
     */
    @RequestMapping(value = "/valueAnnoString", method = RequestMethod.POST)
    public String valueAnnoString() {
        return helloWorld1 + " & " + helloWorld2;
    }
 
 
    /**
     * {@code @Value} 注入List
     *
     * @return
     * @throws NoSuchMethodException
     */
    @RequestMapping(value = "/valueAnnoList", method = RequestMethod.POST)
    public List<Integer> valueAnnoList() {
        return fList;
    }
 
 
    /**
     * 測試通過ExpressionParser調用SpEL表達式
     * @return
     */
    @RequestMapping(value = "/expressionParse", method = RequestMethod.POST)
    public List<Integer> expressionParse() {
 
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
        List<Integer> result1 = parser.parseExpression("{4,5,5,6}").getValue(List.class);
        return result1;
    }
 
 
 
    /**
     * 使用java代碼
     * @return
     */
    @RequestMapping(value = "/javaCode", method = RequestMethod.POST)
    public Integer javaCode() {
 
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
 
        //等同於直接用java代碼,還有方法調用
        String str = parser.parseExpression("new String('Hello World').substring(3)").getValue(String.class);
        log.info("str=={}", str);
 
        //TType 等同於java的Integer.MAX_VALUE
        Integer integer = parser.parseExpression("T(Integer).MAX_VALUE").getValue(Integer.class);
        log.info("integer=={}", integer);
        return integer;
    }
 
 
 
 
    /**
     * 注入並調用method方法
     * @return
     * @throws NoSuchMethodException
     */
    @RequestMapping("methodInvoke")
    private boolean methodInvoke() throws NoSuchMethodException {
        //創建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //獲取java自帶的Integer類的parseInt(String)方法
        Method parseInt = Integer.class.getDeclaredMethod("parseInt", String.class);
        //將parseInt方法注冊在ctx容器內, 推薦這樣使用
        ctx.registerFunction("parseInt", parseInt);
        //再將parseInt方法設為parseInt2
        ctx.setVariable("parseInt2", parseInt);
 
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL語法,比對兩個方法執行完成后,結果是否相同
        String expreString = "#parseInt('2') == #parseInt2('3')";
        //執行SpEL
        Expression expression = parser.parseExpression(expreString);
        Boolean value = expression.getValue(ctx, Boolean.class);
        return value;
    }
 
 
 
    /**
     * 運算符
     * @return
     */
    @RequestMapping(value = "/operator", method = RequestMethod.POST)
    public boolean operator() {
 
        //創建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //將字符串defg放在 ctx容器內
        ctx.setVariable("abc", new String("defg"));
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
        String abc = parser.parseExpression("#abc").getValue(ctx, String.class);
        log.info("abc=={}", abc);
 
        //運算符判斷
        Boolean result = parser.parseExpression("#abc.length() > 3").getValue(ctx, Boolean.class);
        log.info("result=={}", result);
        return result;
    }
 
 
    /**
     * Elvis等用法
     * @return
     */
    @RequestMapping(value = "/elvis", method = RequestMethod.POST)
    public void elvis(){
        //創建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //將字符串defg放在 ctx容器內
        ctx.setVariable("name", null);
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
        String name = parser.parseExpression("#name?:'other'").getValue(ctx, String.class);
        log.info("name=={}",name);
        log.info("saved length() == {}", parser.parseExpression("#name?.lenth()").getValue(ctx));
 
        //將字符串defg放在 ctx容器內
        ctx.setVariable("name", "abc");
        name = parser.parseExpression("#name?:'other'").getValue(ctx, String.class);
        log.info("changed name=={}",name);
        log.info("changed saved length() == {}", parser.parseExpression("#name?.length()").getValue(ctx));
 
 
        //map獲取
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("a", 1);
        ctx.setVariable("map", map);
        int mapA = parser.parseExpression("#map['a']").getValue(ctx, int.class);
        log.info("map['a']=={}", mapA);
        //修改
        parser.parseExpression("#map['a']").setValue(ctx, 6);
        mapA = parser.parseExpression("#map['a']").getValue(ctx, int.class);
        log.info("changed map['a']=={}", mapA);
 
        return ;
    }
 
 
    @RequestMapping("/listFunction")
    private void listFunction() {
        //創建ctx容器
        StandardEvaluationContext ctx = new StandardEvaluationContext();
        //創建ExpressionParser解析表達式
        ExpressionParser parser = new SpelExpressionParser();
 
        //list過濾
        ctx.setVariable("aList",fList);
        List<Integer> cList = parser.parseExpression("#aList.?[#this>3]").getValue(ctx, List.class);
        log.info("filter list=={}", cList);
 
 
        List<Book> books = new ArrayList<>();
        books.add(new Book("JAVA Program", 2000, 102.5));
        books.add(new Book("C Program", 1985, 36));
        books.add(new Book("scala", 2015, 60));
 
        //object過濾
        ctx.setVariable("books", books);
        List<Book> filterBooks1 = (List<Book>) parser.parseExpression("#books.?[year>2000]").getValue(ctx);
        log.info("filterBooks1=={}", filterBooks1);
 
        //投影
        List<String> filterBooksName = parser.parseExpression("#books.?[price<100].![name]").getValue(ctx, List.class);
        log.info("filterBooksName=={}", filterBooksName);
 
        return;
    }
 
 
 
    @Data
    class Book{
        private String name;
        private int year;
        private double price;
 
        public Book(String name, int year, double price) {
            this.name = name;
            this.year = year;
            this.price = price;
        }
    }
 
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM