Spring表達式語言(SpEL)

Spring 表達式語言 Spring Expression Language(簡稱 SpEL )是一個支持運行時查詢和操做對象圖的表達式語言 。 語法相似於 EL 表達式 ,但提供了顯式方法調用和基本字符串模板函數等額外特性 。html

SpEL 雖然做爲 Spring 家族中表達式求值的基礎,但卻能夠被獨立使用。java

1 加入依賴
首先在 pom.xml 中加入依賴:spring

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>${spring.version}</version>
</dependency>

2 簡單示例
在此,咱們定義一個簡單求和的算術表達式,交給 SpEL 解析:express

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("6+2");
Integer result = (Integer) expression.getValue();
System.out.println("result:" + result);

輸出結果:緩存

result:8框架

3 核心接口
SpEL 的相關類和接口都定義在 org.springframework.expression 包、子包和 spel.support 中 。yii

ExpressionParser 接口用於解析表達式字符串 。 Expression 接口用來計算表達式串的值 。 當調用 ExpressionParser#parseExpression()時可能拋出 ParseException ; 當調用 Expression#getValue() 時可能拋出EvaluationException 異常 。ide

SpEL 支持方法調用,訪問屬性和調用構造器等特性。函數

好比,咱們能夠在字符串表達式中加入 concat 方法來拼接字符串:性能

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("'SpEL'.concat(' thinking')");
String result = (String) expression.getValue();
System.out.println("result:" + result);

輸出結果:

result:SpEL thinking

由於 expression.getValue() 返回的是 Object,因此必須進行顯式轉換。咱們可使用 public <T> T getValue(Class<T> desiredResultType),來優雅地獲取相應類型的值。

String result = expression.getValue(String.class);

SpEL 更常應用於獲取某個類實例的屬性值。假設咱們有一個 Account 類:

public class Account {
    private String name;
    public Account(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

咱們經過表達式來獲取 Account 實例中的 name 值:

Account account=new Account("Deniro");
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context=new StandardEvaluationContext(account);
Expression expression = parser.parseExpression("name");
String result = expression.getValue(context,String.class);
System.out.println("result:" + result);

輸出結果:

result:Deniro

能夠在表達式中引用根對象的屬性,在解析表達式時,會在內部使用反射會從根對象中獲取屬性的值。

在單獨使用 SpEL 時,須要建立一個 ExpressionParser 解析器,並提供一個 EvaluationContext 求值上下文 。 但在 Spring 框架中,僅須要配置一個 SpEL 表達式( Spring bean 定義)便可。SpEL 解析器、求值上下文、根對象和其餘預約義變量等基礎設施都會被隱含建立

EvaluationContext 接口

EvaluationContext 接口定義了獲取屬性、方法、域字段解析器及類型轉換器。 它的實現類 StandardEvaluationContext 使用的是反射機制來操做對象 。 爲了提升性能,他會在其內部緩存 java.lang.reflect 的 Method、Field 和 Constructor 實例。

StandardEvaluationContext 類能夠經過構造函數來傳遞求值根對象,也能夠經過方法 setRootObject() 來指定求值根對象。 可使用 setVariable() 來設置變量;使用 registerFunction(String name, Method method)來註冊自定義函數。

在 StandardEvaluationContext 中,咱們還能夠註冊自定義的構造函數轉換器 ConstructorResolvers、方法轉換器 MethodResolvers 及屬性轉換器 PropertyAccessors 來定製表達式的解析方式 。

默認狀況下, SpEL 使用的是 ConversionService 來執行轉換服務 。它會根據源類型與目標類型來選擇合適的轉換器。Spring 內置了不少轉換器:
Spring表達式語言(SpEL)

內置轉換器(部分)

若是這些內置轉換器不能知足業務需求,咱們能夠註冊自定義轉換器。
當表達式中包含泛型時, SpEL 將嘗試將當前值轉換爲對應的目標類型。

public class GenericConvertExample {
    public List<Integer> nums = new ArrayList();

    public static void main(String[] args) {

        GenericConvertExample example = new GenericConvertExample();
        example.nums.add(1);

        //建立表達式上下文
        StandardEvaluationContext context = new StandardEvaluationContext(example);
        //建立表達式解析器
        ExpressionParser parser = new SpelExpressionParser();

        String expression = "nums[0]";
        //自動將 2 轉換爲 Integer 類型
        parser.parseExpression(expression).setValue(context, 2);
        System.out.println("nums:" + example.nums);

        //拋出 ConverterNotFoundException
        try {
            parser.parseExpression(expression).setValue(context, true);
        } catch (EvaluationException e) {
            e.printStackTrace();s
        } catch (ParseException e) {
            e.printStackTrace();
        }

                //literal expressions 
        Expression exp = parser.parseExpression("'Hello World'");
        String msg1 = exp.getValue(String.class);
        System.out.println(msg1);

        //method invocation
        Expression exp2 = parser.parseExpression("'Hello World'.length()");  
        int msg2 = (Integer) exp2.getValue();
        System.out.println(msg2);

        //Mathematical operators
        Expression exp3 = parser.parseExpression("100 * 2");  
        int msg3 = (Integer) exp3.getValue();
        System.out.println(msg3);

        //create an item object
        Item item = new Item("yiibai", 100);
        //test EL with item object
        StandardEvaluationContext itemContext = new StandardEvaluationContext(item);

        //display the value of item.name property
        Expression exp4 = parser.parseExpression("name");
        String msg4 = exp4.getValue(itemContext, String.class);
        System.out.println(msg4);

        //test if item.name == 'yiibai'
        Expression exp5 = parser.parseExpression("name == 'yiibai'");
        boolean msg5 = exp5.getValue(itemContext, Boolean.class);
        System.out.println(msg5);

    }
}

這裏的定義了 List<Integer> 類成員變量,因此咱們能夠直接把整型數值傳入 setValue() 方法。若是傳入一個非整型類型的對象,那麼就會拋出 ConverterNotFoundException 異常。

4 SpelCompiler 編譯器

默認狀況下, SpEL 表達式只有在求值時纔會進行表達式計算,因此表達式能夠在運行時進行動態修改。但若是一個表達式被重複調用的次數不少,那麼就必須使用
SpelCompiler 編譯器來保證性能。

SpelCompiler 編譯器會將表達式編譯成字節碼,只有在運行時表達式發生變化時,纔會被從新編譯。

SpelCompiler 編譯器適用於被調用的頻率較高且表達式不常常發生變化的場景。

//Spel 解析配置器
SpelParserConfiguration configuration=new SpelParserConfiguration
        (SpelCompilerMode.IMMEDIATE,CompilerExample.class.getClassLoader());

//解析器
SpelExpressionParser parser=new SpelExpressionParser(configuration);

//上下文
EvaluationContext context=new StandardEvaluationContext(new Account("Deniro"));

//表達式
String expression="getName()";

//解析表達式
SpelExpression spelExpression=parser.parseRaw(expression);

System.out.println(spelExpression.getValue(context));

咱們經過指定編譯模式和類加載器來新建 Spel 解析配置器。

Spel 解析配置器有三種編譯模式(SpelCompilerMode):

編譯模式 說明
OFF 不啓用編譯模式(默認)。可在 spring.properties 中經過 spring.expression.compiler.mode 屬性進行全局設置。
MIXED 在混合模式下,隨着時間的推移,表達式會從解釋模式自動切換到編譯模式。即前面使用解釋模式,當調用次數達到某個閾值後,改成使用編譯模式。
IMMEDIATE 啓用編譯模式。實際內部在調用第一次以後,纔會真正使用編譯模式。

本文展現了Spring表達式解析器的一些基本用法,訪問官方Spring表達文檔,使用SpEL的例子。

相關文章
相關標籤/搜索