mvel語法指南[翻譯]


mvel受到了java語法的啟發,但是存在一些根本性的差異,mvel旨在使其成為更有效的表達式語言.比如直接支持集合、數組和字符串匹配,正則表達式的運算操作等.
mvel2.x有以下幾個部分組成: 
  • Property expressions
  • Boolean expressions
  • Method invocations
  • Variable assignments
  • Function definitions
目錄:
     1:基本語法
     2:操作運算
     3:值測試
     4:內聯list,maps,arrays 
     5:屬性導航
     6:字面量值
     7:字面類型
     8:控制流程
     9:
 

一、基本語法:

1:簡單的屬性表達式:
  user.name
  user.name == 'John Doe'
和java一樣,mvel支持運算符優先級規則的完整的策略,包括使用括號的能力來控制執行順序。
  (user.name == 'John Doe') && ((x * 2) - 1) > 20
 
2:復合表達式
可以用任意數量的語句,用分號表示終止聲明。
statement1; statement2; statement3
3:返回值:
  mvel使用最后一個值作為返回值,開發人員不必顯示的指定返回值, 默認為最后一個值最為返回
 
a = 10;
b = (a = a * 2) + 10;
a;

二、mvel2.0的操作運算

下面給出的是mvel的現有的所有操作運算符合運算邏輯

Unary Operators

Operator

Description

Example

new

Object instantiation

new String("foo")

with

Block WITH Operator. Perform multiple operations on a single object instance

with (value) { name = 'Foo', age = 18, sex = Sex.FEMALE }

assert

Assert that a value is true or fail with an AssertionError

assert foo != null

isdef

Tests whether or not a variable is defined within the scope

isdef variableName

!

Boolean negation operator

!true == false

Comparison Operators

Operator

Description

Example

==

Equality Check.  Checks to see if the values on both sides of the operator are equal.  Unlike Java, this is not an identity check.  

"foo" == "foo" is true 

!=

Not Equals Check.  Checks to see if the values on both sides of the operator are not equal. 

"foo" == "bar" is false 

>

Greater Than Check.  Checks to see if the value on the left side of the operator is greater than than value on the right. 

2 > 1 is true 

<

Less Than Check.  Checks to see if the value on the left side of the operator is less than value on the right. 

1 < 2 is true 

>= 

Greater Than or Equal.  Checks to see if the value on the left hand side is greater than or equal to the value on the right. 

1 >= 1 is true 

<=

Less Than or Equal.  Checks to see if the value on the left hand side is less than or equal to the value on the right. 

2 <= 2 is true 

contains

Value Containment Check.  Checks to see if the value on the left contains the value on the right.  For more details on how this operator works, see Contains Operator 

var contains "Foo" 

is orinstanceof

Type Checking Operator.  Checks to see if the value on the left is a member of the class on the right.  

var instanceof Integer 

strsim

String Similarity Check.  Compares to strings and returns a similarity between them as a percentage. See: String Similarity Check

"foobie" strsim "foobar" 

soundslike

Soundex Check.  Performs a soundex comparison between two strings. See: Soundex

"foobar" soundslike "fubar" 

Logical Operators

Operator

Description 

Example

&&

Logical AND.  Checks to see that the values on both sides of the operator are true. 

foo && bar 

||

Logical OR.  Checks to see if either the value on the left or the right is true. 

foo || bar 

or 

Chained OR.  Checks a sequence of values for emptinessand returns the first non-empty value. (This operator, or at least it's equivalent functionality, is referred to as the "elvis operator" in other languages) 

foo or bar or barfoo or 'N/A' 

~=

Regular Expression Match.  Checks to see if the value on the left matches the regular expression on the right. 

foo ~= '[a-z].+'

Bitwise Operators

Operator

Description 

Example 

&

Bitwise AND. 

foo & 5

|

Bitwise OR. 

foo | 5

^

Bitwise XOR. 

foo ^ 5

Arithmetic Operators

Operator

Description 

Example



Addition.  Adds the value on the left to the value on the right 

1 + 2 

-

Subtraction.  Subtracts the value on the right from the value on the left 

2 - 1 

/

Division.  Divides the number on the left by the number on the right 

2 / 1 



Multiplication.  Multiples the number on the left by the number on the right 

1 * 2 



Modulus.  Divides the number on the left by the number on the right and returns the remainder. 

2 % 1 

Other Operators

Operator

Description 

Example 

+

String Concatenation.  Overloaded operator for concatenating two strings together. 

"foo" + ;bar" 

#

Concatenation Operator.  Concatenates two literals as strings. 

1 # 2returns "12" 

in

Projection/Fold. Projects across a collection.  See:Projections and Folds

(foo in list)



Assignment.  Assigns the value on the right to the variable on the left. 

var = "foobar" 

 
 

三、值測試 

在mvel中所有的比較都是值比較而不是比較句柄,所有foo=='bar'和java中的equals是相同的

1:判斷值是否為emptiness

MVEL提供了一個特殊的字符來表示值為emptiness的情況,叫作empty,如:foo == empty,若foo滿足emptiness的任何條件,這個表達式值都為true

For example:

foo == empty

若foo滿足emptiness的任何條件,這個表達式值都為true

2:測試null

     mvel中可以使用null或者nil來進行空值判斷
foo == null;
foo == nil; // same as null

3:強制轉換

     右邊的值會強制轉換為左邊的值在比較的時候:
"123" == 123;
結果為true,因為在比較的時候123被強制轉換成string在進行值比較
 

四、內置集合 

mvel可以通過簡單的語法創建集合
['Bob' : new Person('Bob'), 'Michael' : new Person('Michael')]
以上代碼效果和下面一樣
Map map = new HashMap();
map.put("Bob", new Person("Bob"));
map.put("Michael", new Person("Michael"));
這是一個非常強大的方式來表達MVEL內的數據結構。你可以在任何地方使用這些構造,比如作為方法的參數:
something.someMethod(['foo' : 'bar']); 

Lists

列表用下面的格式來描述: [item1, item2, ...]

For example:

["Jim", "Bob", "Smith"]

Maps

Map用下面的格式來描述: [key1 : value1, key2: value2, ...]

For example:

["Foo" : "Bar", "Bar" : "Foo"]

Arrays

數組用下面的格式來描述: {item1, item2, ...}

For example:

{"Jim", "Bob", "Smith"}
數組的強制轉換
你可以進行如下操作:
foo.someMethod({1,2,3,4});
正如你看到的,雖然你沒有定義數組的類型,但是在調用foo方法的時候,會進行轉換,比如foo的參數為int[],表達式會進行相應的轉換.
 

五、屬性訪問

MVEL屬性訪問遵循其他語言,比如Groovy OGNL,EL等的獲取屬性對象。不像其他語言需要取決於底層的訪問方法,MVEL提供了一個單一的、統一的語法來訪問屬性,map,list,等等。
在java中,我們通過以以下方式獲取數據
user.getManager().getName();
在mvel中你可以使用如下方式
user.manager.name
 
你還可以進行控制空值 
在java中:
if (user.manager != null) { return user.manager.name; } else { return null; }
在mvel中
user.?manager.name
 
訪問arraylist
mvel中
user[6]
user.get(6)
 
訪問map:
mvel中
user["foobar"]
 
java中
user.get(foobar)
 
For Maps that use a String as a key, you may use another special syntax:
user.foobar
 
當Map的key是String類型時,還可以使用特殊的方式來訪問,如:user.foobar,也就是允許你把map本身看成一個虛擬的對象,來訪問其屬性
 

字符串作數組

為了能使用屬性的索引(迭代也是如此),所有的字符串都可以看成是一個數組,在MVEL中你可以用下面的方式來獲取一個字符串變量的第一個字符:

foo = "My String";
foo[0]; // returns 'M'
6 MVEL 2.0 Literals

A literal is used to represent a fixed-value in the source of a particular script.

六、字面量literals

String literals

在腳本語言中,一段文字用來代表一個固定的值 

字符串常量:

字符串常量可以用一對單引號或一對雙引號來界定。如:

"This is a string literal"
'This is also string literal'

字符串中的特殊字符

  • \\ - Double escape allows rendering of single backslash in string.
  • \n - Newline
  • \r - Return
  • \u#### - Unicode character (Example: \uAE00)
  • \### - Octal character (Example: \73)

數字常量 

整數可以表示為十進制(基數為10),8進制(基數為8),或十六進制(基數為16)。

一個十進制數字,不從零開始(相對於8進制、16進制而言),可以表示任意數,如:125

125 // decimal

一個八進制數,以0為前綴,后面跟着0到7內的數字

0353 // octal

一個十六進制,以0X為前綴,后面可以跟着0-9,A-F范圍內的數字

0xAFF0 // hex

浮點常量

A floating point number consists of a whole number and a factional part denoted by the point/period character, with an optional type suffix.

10.503 // a double
94.92d // a double
14.5f // a float

二進制常量

You can represent BigInteger and BigDecimal literals by using the suffixes B and I(uppercase is mandatory).

104.39484B // BigDecimal
8.4I // BigInteger

布爾常量

Boolean literals are represented by the reserved keywords true and false.

Null 常量

The null literal is denoted by the reserved keywords null or nil.

內部類

和java類似,訪問內部類:$
           org.proctor.Person$BodyPart 
 

七、控制流程 

if和else 和java相同
if (var > 0) {
   System.out.println("Greater than zero!");
}
else if (var == -1) { 
   System.out.println("Minus one!");
}
else { 
   System.out.println("Something else!");
}
 
支持三目運算符
var > 0 ? "Yes" : "No";
 
嵌套的三目運算
var > 0 ? "Yes" : (var == -1 ? "Minus One!" : "No")
迭代:
count = 0;
foreach (name : people) {
   count++;
   System.out.println("Person #" + count + ":" + name);
}
    
System.out.println("Total people: " + count);
支持字符串
     str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
foreach (el : str) {
   System.out.print("[" + el + "]"); 
}
輸出結果:[A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]
 
支持數字:
foreach (x : 9) { 
   System.out.print(x);
} 
現在也支持for關鍵字

For Loop

MVEL 2.0 implements standard C for-loops:

for (int i =0; i < 100; i++) { 
   System.out.println(i);
}

Do While, Do Until

和java中的一樣,MVEL也實現了Do While,Do Until,While和Until意義正好相反。
do { 
   x = something();
} 
while (x != null);

... is semantically equivalent to ...

do {
   x = something();
}
until (x == null);

While, Until

MVEL 2.0 implements standard while, with the addition of the inverse until.

while (isTrue()) {
   doSomething();
}

... or ...

until (isFalse()) {
   doSomething();
}

八、投影和交集 

假設user是一個集合對象,要獲取user集合中所有parent的name可以通過以下途徑
parentNames = (parent.name in users);
假設user對象有一個集合成員familyMember,你可以訪問該集合
familyMembers = (name in (familyMembers in users));

九、賦值

MVEL允許你對表達式中的變量進行賦值,以便在運行時獲取,或在表達式內部使用。因為MVEL是動態類型語言,所以你不必為了聲明一個變量
而指定其類型。當然,你也可以選擇指定。
str = "My String"; // valid
String str = "My String"; // valid

與java語言不同的是,當給一個指定類型的變量賦值時,MVEL會提供自動的類型轉換(可行的話),如:
String num = 1;
assert num instanceof String && num == "1";
對於動態類型變量而言,你要想對其進行類型轉換,你只需要將值轉換成相應的類型既可:
num = (String) 1;
assert num instanceof String && num == "1";

Filters

你可以進行過濾
(doSomeMethod() in listOfThings if $.shouldBeRun())
$代表了前面迭代中的每一個item
 
你也可以用它作為一個返回成員
($ in fooList if $.name contains 'foobie')
其他例子:
(toUpperCase() in ["foo", "bar"]); // returns ["FOO", "BAR"]
(($ < 10) in [2,4,8,16,32]);       // returns [true, true, true, false, false]
($ in [2,4,8,16,32] if $ < 10);     // returns [2,4,8]
下面是一個快排的實現:  

/** * Sample MVEL 2.0 Script * "Functional QuickSort" * by: Christopher Michael Brock, Inspired by: Dhanji Prasanna */ import java.util.*; // the main quicksort algorithm def quicksort(list) { if (list.size() <= 1) { list; } else { pivot = list[0]; concat(quicksort(($ in list if $ < pivot)), pivot, quicksort(($ in list if $ > pivot))); } } // define method to concatenate lists. def concat(list1, pivot, list2) { concatList = new ArrayList(list1); concatList.add(pivot); concatList.addAll(list2); concatList; } // create a list to sort list = [5,2,4,1,18,10,15,1,0]; // sort it! quicksort(list)

十、函數

MVEL可以使用def或function關鍵字來定義本地函數。
函數必須是先聲明后引用,唯一例外的是遞歸調用的時候。

1、簡單示例
定義函數:
def hello() { System.out.println("Hello!"); }
定義了一個沒有參數的函數hello.當調用該函數時會在控制台打印"Hello!". An MVEL-defined function works just like any regular method call,

 and resolution preference is to MVEL functions over base context methods.

hello(); // calls function
2、傳參和返回值

函數可以接收參數和返回一個值,看下面的例子:
def addTwo(a, b) { 
   a + b;
}

這個函數會接收兩個參數(a和b),然后將這兩個變量相加。因為MVEL遵循last-value-out原則,所以
結果將會被返回。因此,你可以這樣來使用這個函數:
val = addTwo(5, 2);
assert val == 10;
當然,也可以使用return 關鍵字來強迫從程序內部返回一個函數值。
3、closures
MVEL支持closure,雖然,其功能與本地java函數沒有任何關聯。
// define a function that accepts a parameter    
def someFunction(f_ptr) { f_ptr(); }
// define a var
var a = 10;
// pass the function a closure
someFunction(def { a * 10 }); 

十一、Lambda表達式

MVEL允許定義Lambda方法,如下所示:
threshold = def (x) { x >= 10 ? x : 0 }; 
result = cost + threshold(lowerBound);
上面的例子定義了一個Lambda,並將其賦值給變量"threshold".Lambda實質上就是一個用來給變量賦值的函數,也是closure

十二、攔截器

MVEL提供了在編譯后的表達式里使用攔截器的功能,這對實現監聽器或是在表達式內部觸發一個外部事件特別有用。聲明攔截器用的是@Syntax,
有點像java語言中的注解。攔截器的聲明應該放在待封裝的語句之前,它可以實現之前或之后的監聽器,或二者都實現。例如:

@Intercept
foreach (item : fooItems) { 
   total += fooItems.price;
}
在這個特殊的句子里,攔截器封裝了整個的foreach塊,因此,如果攔截器實現了之后的監聽器,則當foreach循環結束后,攔截動作將被觸發。

1、攔截器接口org.mvel.intergration.Interceptor 

public interface Interceptor {
    public int doBefore(ASTNode node, VariableResolverFactory factory);
    public int doAfter(Object exitStackValue, ASTNode node, VariableResolverFactory factory);
}
攔截器接口提供了兩個待實現的方法:doBefore和doAfter,下面我們來看一下MVEL運行時傳遞給這兩個方法的參數的含義


2、doBefore
在執行封裝的命令前會執行doBefore方法。
org.mvel.ASTNode::node	
 ASTNode句柄是 攔截器內部ASTNode 的一個引用,可以用來獲取實際編譯后的代碼的信息。
org.mvel.integration.VariableResolverFactory::factory
變量分析器工廠提供表達式內當前范圍內變量的訪問權限。 
3、doAfter
在執行完封裝的指令后執行doAfter方法
java.lang.Object::exitStackValue
doAfter方法雖是在語句執行后執行,但卻不是在幀結束前。因此,操作結束時留在棧中的任何數據都仍然存在,而且能被攔截器訪問。例如:
@Intercept cost += value;
這是一個比較特殊的句子,cost的原值一直保存在棧中,直到整個幀執行完畢,因此,這個值在調用doAfter方法時可以通過exitStackValue訪問到。
org.mvel.ASTNode::node
這是傳遞到doBefore方法中的同一個AST 元素,更多細節參考doBefore方法。
org.mvel.intergration.VariableResolverFactory::factory
同doBefore方法
4、編譯器中使用攔截器
為了能是攔截器連到表達式中,必須在編譯表達式之前提供攔截器,因此有一點需要注意,攔截器可能不用於MVEL解釋器。
攔截器是儲存在map里提供給編譯器的,map中的鍵為攔截器的名稱,值為攔截器實例。如:
// Create a new ParserContext
ParserContext context = new ParserContext();
Map<String, Interceptor> myInterceptors = new HashMap<String, Interceptor>();
// Create a simple interceptor.
Interceptor myInterceptor = new Interceptor() {
    public int doBefore(ASTNode node, VariableResolverFactory factory) {
        System.out.println("BEFORE!");
    }

    public int doAfter((Object value, ASTNode node, VariableResolverFactory factory) {
        System.out.println("AFTER!");
    }
};

// Now add the interceptor to the map.
myInterceptors.put("Foo", myInterceptor);

// Add the interceptors map to the parser context.
context.setInterceptors(myInterceptors);

// Compile the expression.
Serializable compiledExpression = MVEL.compileExpression(expression, context);

十四、數據類型

MVEL是一種有靜態類型的動態類型語言。大部分MVEL使用者都比較傾向於用動態類型,因為它非常簡單易用。如:

a = 10; // declare a variable 'a'
b = 15; // declare a variable 'b';

a + b;

1、動態類型與強制轉換

像MVEL這種直接與java對象(靜態類型)打交道的語言,最重要的一個方面就是強制類型轉換。因為MVEL不能對一個java.lang.String對象和一個

java.lang.Integer對象進行數學運算,所以就必須把其中一個的類型轉換成另一個的類型。

2、性能考慮
在你的應用中集成一個像MVEL這樣的東西,性能考慮是必須的。對於重量級程序加載,強制類型轉換超負荷等可以通過緩存和優化器(僅用於預編譯
的表達式)來解決。然而,並不是所有的強制類型轉換都可以忽略不管,關鍵要看它是在做什么。

比如,當一個String類型的變量在運行中要看成一個整形變量時,要阻止運行時將字符串轉換成整型簡直是不可能的,像這種情況,一定要考慮其性能。

3、方法調用
調用方法是強制轉換的最重要的一個方面。從根本上講,你可以直接調用,而無需關心參數是什么。解釋器或編譯器會分析方法的參數類型,然后確定
要進行哪一種強制轉換,如果是重載的方法,它會選擇與輸入類型最接近的那個方法進行調用,以盡可能的避免強制轉換。 

4、數組

數組是強制類型轉換中最有趣的一個方面,因為MVEL缺省使用無類型數組(也就是說任何情況下都是Object[]),只有當遇到類型沖突時,才會嘗試將

整個數組轉換成所需的類型,比如在方法調用傳參時。

示例:

myArray = {1,2,3};

// pass to method that accepts String[]
myObject.someMethod(myArray);

在這個例子里,somMethod方法接收字符數組,這在MVEL中不會出錯,相反,MVEL會將myArray轉換成字符數組。

5、靜態類型

靜態類型與java類似,只不過默認情況下仍然會進行強制轉換。

int num = 10;

這個句子聲明了一個整型變量num,這時,MVEL運行時會強制轉換類型。比如,聲明后賦值一個不合適類型的數據,結果就會出現異常。

num = new HashMap(); // will throw an incompatible typing exception.

但如果是一個可以進行強制類型轉換的值時,MVEL就會進行強制轉換。

num = "100"; // will work -- parses String to an integer.
6、嚴格類型

嚴格類型是編譯器的一種可選模式,在這種模式下,所有的類型都必須限定,不管是聲明時還是在引用時。

啟動嚴格模式:

當編譯一個表達式時,可以通過ParserContext設置setStrictTypeEnforcement(true)將編譯器設置成嚴格模式。

嚴格類型通過表達式內的具體類型聲明或提前告訴轉換器確定的類型來完成。例如:

ExpressionCompiler compiler = new ExpressionCompiler(expr);

ParserContext context = new ParserContext();
context.setStrictTypeEnforcement(true);

context.addInput("message", Message.class);
context.addInput("person", Person.class);

compiler.compile(context);
在這個例子中我們通知編譯器表達式將接收兩個外部輸入:message 和 person 及它們的類型。這就使得編譯器可以在編譯時確定某一

個調用是否是安全的,從而防止了運行時的錯誤。

7、強類型

強類型是MVEL2.0新引入的概念。強類型模式要求所有的變量必須是限定的類型,從這一點上它與嚴格類型不同。差別在於嚴格模式只是在編譯時

限定屬性和方法調用的類型。


十五、Shell

通過交互式的Shell,你可以直接與MVEL打交道,去探究MVEL的特性。

1、運行Shell

只需運行MVEL的分布式jar包既可運行Shell:java -jar mvel2-2.0.jar
或者,你也可以在你喜歡的IDE中通過配置一個該類的運行環境來運行。


十六、FAQ

1、為什么不能使用.class的引用?
MVEL沒有像java中的用來執行類型文件的.class標識符,其實它本身就沒有class文件,而只需要通過其名稱就可以引用這個類。比如,一個方法
接收一個Class類型作為參數,你可以這樣來調用:

// MVEL
someMethod(String);

// Java-equivalent
someMethod(String.class);
事實上,MVEL將.class視作一個普通的bean屬性,因此,如果使用String。class,那返回值就會是指向java.lang.Class本身的一個
java.lang.Class 的實例,因此就相當於在java中使用String.class.getClass() .

原理是這樣的,MVEL使用動態類型系統,這樣類型就被當作普通的變量來看待,而不是像java中限定類文件。所以,MVEL允許class類型作為

一個普通變量來引用,而不像java。

十七、為什么不能用object.class.name的格式?

這是MVEL的一個限制,可能會在將來的某個版本中標記出來,但bean屬性不支持對Class的引用。並不是說不能調用Class的方法,你必須使用

限定的方法,像:

someVar.class.getName();     // Yes!
someVar.class.name;          // No!

someVar.getClass().getName() // Yes!
someVar.getClass().name      // No!
這一規定完全限制了java.lang.Class僅可用做某個變量的屬性,並限制了MVEL處理類的引用的方式。

 

https://github.com/imona/tutorial/wiki/MVEL-Guide

http://blog.csdn.net/y461517142/article/details/17926055


免責聲明!

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



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