天天看點

Spring SpELSpring SpEL

Spring SpEL

文章目錄

  • Spring SpEL
    • 一、JVM動态語言
    • 二、SpEL表達式
    • 三、SpEL的核心接口
      • 3.1 `ExpressionParser`
        • (1) 字元串的拼接
      • 3.2 `EvaluationContext`
        • (2) 針對特定執行個體對象的屬性進行求值
      • 3.3 `SpelCompiler`可以将位元組碼直接編譯成位元組碼
    • 四、SpEL表達式
      • 4.1 文本字元解析
      • 4.2 對象屬性解析
      • 4.3 數組、集合類型解析
      • 4.4 方法解析
      • 4.5 操作符解析
        • (1) 關系操作符
        • (2)邏輯操作符
        • (3)算術運算操作符
      • 4.6 安全導航運算符(避免了空指針對象驗證)
      • 4.7 三元運算符
      • 4.8 `Elvis`運算符
      • 4.9 指派、類型、構造器、變量
      • 4.10 集合過濾
      • 4.11 集合轉換
    • 五、在Spring中使用SpEL
      • 5.1 基于XML配置
      • 5.2 基于注解的配置

一、JVM動态語言

作用:表達式語句的動态解析。

項目中的實踐:在項目開發中,遇到一種場景,需要根據公式計算資料,而涉及到很多公式,并且公式可能會有變動。是以,在開發中我就使用到了動态解析這種方案——将計算公式存到表中。能夠非常靈活地計算出結果。

Java6.0 中内置內建了Mozila Rhino的JavaScript解析引擎,是以可以很友善地在Java中調用JavaScript函數。

動态求和函數:

public static void main(String[] args) throws ScriptException, NoSuchMethodException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        String scriptText = "function sum(a,b){ return a+b;}";
        engine.eval(scriptText);
        Invocable invocable = (Invocable) engine;
        Object result = invocable.invokeFunction("sum", 100, 99);
        System.out.println(result instanceof Double);
        System.out.println(result);
    }
           

二、SpEL表達式

對于僅僅需要一些簡單的表達式需求的場景,使用腳本語言顯得有些“笨重”,這就是介紹SpEL表達式的原因。

  • SpEL具有顯式方法調用和基本字元串模闆函數等特性;
  • SpEL不直接依賴于Spring架構,可以獨立使用;
    可以将SpEL作為獨立的動态語言使用

使用前需要引入

spring-expression

:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${spring.version}</version>
</dependency>
           
public static void main(String[] args) {
    ExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression("'hello' + ' world'");
    String message= (String) expression.getValue();
    System.out.println(message);
}
           

三、SpEL的核心接口

3.1

ExpressionParser

(1) 字元串的拼接

private static void testStrConcat(){
    ExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression("'HelloWorld'.concat('!')");
    String value = (String)expression.getValue();
    System.out.println(value);
    System.out.println(expression.getValue(String.class));
}
           

3.2

EvaluationContext

(2) 針對特定執行個體對象的屬性進行求值

建立

StandardEvaluationContext

執行個體時,指定一個跟對象作為求值的目标對象。

private static void objPropertiesValue(){
    User user = new User();
    user.setUserName("XiaoMing");
    user.setCredits(100);
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext(user);
    Object userName = parser.parseExpression("userName").getValue(context);
    System.out.println(userName);
}
           

3.3

SpelCompiler

可以将位元組碼直接編譯成位元組碼

  • 由于

    SpelCompiler

    直接将表達式編譯成位元組碼,避免了每次調用時的文法解析消耗
public static void main(String[] args) {
        User user = new User();
        // 建立解析配置
        SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
                CompilerSample.class.getClassLoader());
        // 建立解析器
        SpelExpressionParser parser = new SpelExpressionParser(configuration);

        // 建立擷取上下文
        EvaluationContext context = new StandardEvaluationContext(user);

        // 表達式
        String expression = "isVipMember('tom') && isVipMember('pip')";

        // 解析表達式
        Expression spELExpression = parser.parseRaw(expression);

        // 通過表達式求值
        System.out.println(spELExpression.getValue(context));
        System.out.println(spELExpression.getValue(context));
    }
           

四、SpEL表達式

4.1 文本字元解析

文本表達式支援字元串、日期、數字(正數、實數及十六進制數)、布爾類型及null。

public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();

        String helloWorld = parser.parseExpression("'Hello World'").getValue(String.class);

        double doubleNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
        System.out.println(doubleNumber);// 6.0221415E23
        
    }
           

4.2 對象屬性解析

使用類似“xxx.yyy.zzz”的對象屬性路徑輕松通路對象屬性值。

4.3 數組、集合類型解析

在SpEL中,支援數組、集合類型(Map、List)的解析。

4.4 方法解析

  • 支援Java可通路方法。私有方法是不能調用的;
  • 可以調用String類型的所有可通路的方法;

4.5 操作符解析

(1) 關系操作符

> >= < <= ==

、正規表達式、

instanceof

操作符

(2)邏輯操作符

and 或 &&

or 或 ||

、非操作

!

(3)算術運算操作符

  • 加法運算可用于數字、字元串、日期;
  • 減法運算可用于數字和日期;
  • 乘法和除法運算符僅用于數字;
  • 還支援

    %

    、指數幂

    ^

4.6 安全導航運算符(避免了空指針對象驗證)

格式為在“.”前面添加一個“?”

如果user為空,則直接傳回null,如果不為空,則擷取屬性值。

"user?.userName"
           

4.7 三元運算符

public static void main(String[] args) {
        User user = new User();
        user.setUserName("Tom");

        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext(user);

        String result = parser.parseExpression("userName=='Tom'?'hello '+userName:'not Tom'").getValue(context, String.class);
        System.out.println(result);
    }
           

4.8

Elvis

運算符

<var>?:<value>

如果左邊變量取值為null,就取value值,否則就取var變量自身的值。

user.setUserName(null);
        String elvisValue = parser.parseExpression("userName?:'empty Username'").getValue(context, String.class);
        System.out.println(elvisValue);
           

4.9 指派、類型、構造器、變量

指派

public static void main(String[] args) {
        User user = new User();
        user.setUserName("Tom");
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext(user);
        // 通過 setValue指派
        parser.parseExpression("userName").setValue(context, "XiaoMing");
        System.out.println(user.getUserName());
        // 通過 表達式指派
        parser.parseExpression("userName='xiaoHong'").getValue(context);
        System.out.println(user.getUserName());
    }
           

類型

  • 如果加載的目标類位于java.lang包下,則可以不帶包名;
  • T操作符還可以直接調用類靜态方法;
public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();

        Class stringClass = parser.parseExpression("T(java.lang.String)").getValue(Class.class);
        System.out.println(stringClass == String.class);

        //  直接調用靜态方法
        Object randomValue = parser.parseExpression("T(java.lang.Math).random()").getValue();
        System.out.println(randomValue);
    }
           

構造器

public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        User value = parser.parseExpression("new com.hef.beans.User('aaa')").getValue(User.class);
        System.out.println(value);
    }
           

變量

// 設定一個變量
EvaluationContext context = new StandardEvaluationContext(value);
context.setVariable("oneUserName", "Tom");
parser.parseExpression("userName=#oneUserName").getValue(context);
System.out.println(value.getUserName());
           

4.10 集合過濾

public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();
        // 過濾list
        List<Integer> valueList = new ArrayList<>();
        valueList.addAll(Arrays.asList(2,3,6,10,5,11,13));
        context.setVariable("list", valueList);
        List<Integer> value = (List<Integer>) parser.parseExpression("#list.?[#this>5]").getValue(context);
        System.out.println(value);
        // 過濾map
        Map<String, Integer> map = new HashMap<>();
        map.put("aa", 23);
        map.put("bb", 12);
        map.put("cc", 6);

        context.setVariable("map", map);
        // 過濾出子集
        Map<String, Integer> map1= (Map<String, Integer>) parser.parseExpression("#map.?[value>7]").getValue(context);
        System.out.println(map1);
        // 取第一個值
        Map<String, Integer> map2= (Map<String, Integer>) parser.parseExpression("#map.^[value>7]").getValue(context);
        System.out.println(map2);
        // 取最後一個值
        Map<String, Integer> map3= (Map<String, Integer>) parser.parseExpression("#map.$[value>7]").getValue(context);
        System.out.println(map3);
    }
           

4.11 集合轉換

// 集合的轉換
        List<Boolean> booleanList= (List<Boolean>) parser.parseExpression("#list.![#this>10]").getValue(context);
        System.out.println(booleanList);
           

五、在Spring中使用SpEL

5.1 基于XML配置

5.2 基于注解的配置

#{properties['password']}