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 內置了不少轉換器:
內置轉換器(部分)
若是這些內置轉換器不能知足業務需求,咱們能夠註冊自定義轉換器。
當表達式中包含泛型時, 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的例子。