天天看點

Spring系列之Spring表達式語言(SpEL)

概述

Spring Expression Language,SpEL,類似于Struts2x中使用的OGNL表達式語言,及JSP的EL,能在運作時建構複雜表達式、存取對象圖屬性、對象方法調用等。表達式語言給靜态Java語言增加動态功能。SpEL是單獨子產品,隻依賴于core子產品,可單獨使用,依賴如下,但是一般無需獨立引入,作為spring核心元件,常被其他元件作為基礎依賴,實作若幹功能:如配合配置Bean定義。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
</dependency>      

所有EL都是以​

​${​

​​為起始、以​

​}​

​​為結尾的。EL提供​

​.​

​​和​

​[]​

​兩種運算符來導航資料。

SpEL支援如下表達式:

  1. 基本表達式:字面量表達式、關系,邏輯與算數運算表達式、字元串連接配接及截取表達式、三目運算及Elivis表達式、正規表達式、括号優先級表達式;
  2. 類相關表達式:類類型表達式、類執行個體化、instanceof表達式、變量定義及引用、指派表達式、自定義函數、對象屬性存取及安全導航表達式、對象方法調用、Bean引用;
  3. 集合相關表達式:内聯List、内聯數組、集合,字典通路、清單,字典,數組修改、集合投影、集合選擇;不支援多元内聯數組初始化;不支援内聯字典定義;
  4. 其他表達式:模闆表達式。

注:SpEL表達式中的關鍵字不區分大小寫。

通過執行個體分析SpEL求表達式值的步驟:

public class SpELTest {
    @Test
    public void helloWorld() {
        // 1.建立解析器, 提供SpelExpressionParser預設實作
        ExpressionParser parser = new SpelExpressionParser();
        // 2.解析表達式, parseExpression方法解析得到Expression對象
        Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");
        // 3.構造上下文, 設定資料
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("end", "!");
        // 4.Expression接口的getValue方法
        Assert.assertEquals("Hello World!", expression.getValue(context));
    }
}      

SpEL原理及接口

  1. 表達式:表達式是表達式語言的核心,是以表達式語言都是圍繞表達式進行的,“幹什麼”;
  2. 解析器:用于将字元串表達式解析為表達式對象,“誰來幹”;
  3. 上下文:表達式對象執行的環境,該環境可能定義變量、定義自定義函數、提供類型轉換等等,“在哪幹”;
  4. 根對象及活動上下文對象:根對象是預設的活動上下文對象,活動上下文對象表示了目前表達式操作的對象,“對誰幹”。
Spring系列之Spring表達式語言(SpEL)

核心流程:

  • SpelExpressionParser解析器内部使用Tokenizer類進行詞法分析,即把字元串流分析為記号流,記号在SpEL使用Token類來表示;
  • 得到記号流後,解析器便可根據記号流生成内部抽象文法樹;
  • 在SpEL中文法樹節點由SpelNode接口實作代表:如OpPlus表示加操作節點、IntLiteral表示int型字面量節點;
  • 使用SpelNodel實作組成抽象文法樹;
  • 對外提供Expression接口來簡化表示抽象文法樹,進而隐藏内部實作細節,并提供getValue簡單方法用于擷取表達式值;
  • SpEL提供預設實作為SpelExpression;
  • 定義表達式上下文對象(可選),SpEL使用EvaluationContext接口表示上下文對象,用于設定根對象、自定義變量、自定義函數、類型轉換器等,SpEL提供預設實作StandardEvaluationContext;
public interface ExpressionParser {
  Expression parseExpression(String expressionString);
  Expression parseExpression(String expressionString, ParserContext context);
}      

EvaluationContext接口:

public interface EvaluationContext {
  /**
   * Return the default root context object against which unqualified
   * properties/methods/etc should be resolved. This can be overridden
   * when evaluating an expression.
   */
  TypedValue getRootObject();

  /**
   * Return a list of accessors that will be asked in turn to read/write a property.
   */
  List<PropertyAccessor> getPropertyAccessors();

  /**
   * Return a list of resolvers that will be asked in turn to locate a constructor.
   */
  List<ConstructorResolver> getConstructorResolvers();

  List<MethodResolver> getMethodResolvers();

  @Nullable
  BeanResolver getBeanResolver();

  /**
   * Return a type locator that can be used to find types, either by short or
   * fully qualified name.
   */
  TypeLocator getTypeLocator();

  /**
   * Return a type converter that can convert (or coerce) a value from one type to another.
   */
  TypeConverter getTypeConverter();

  /**
   * Return a type comparator for comparing pairs of objects for equality.
   */
  TypeComparator getTypeComparator();

  /**
   * Return an operator overloader that may support mathematical operations
   * between more than the standard set of types.
   */
  OperatorOverloader getOperatorOverloader();

  void setVariable(String name, @Nullable Object value);

  /**
   * Look up a named variable within this evaluation context.
   * @param name variable to lookup
   * @return the value of the variable, or {@code null} if not found
   */
  @Nullable
  Object lookupVariable(String name);

}      

預設實作是StandardEvaluationContext類,使用setRootObject方法來設定根對象,使用setVariable方法來注冊自定義變量,使用registerFunction來注冊自定義函數等。

​SpringExpressions.g​

​​檔案,​

​.g​

​字尾名檔案,采用Glagol DSL文法寫的配置檔案。

參考

繼續閱讀