MVEL简介
MVEL为 MVFLEX Expression Language(MVFLEX表达式语言)的缩写,它是一种动态/静态的可嵌入的表达式语言和为Java平台提供Runtime(运行时)的语言。
最初是作为一个应用程序框架实用程序的语言开始,该项目现已发展完全独立。
MVEL通常用于执行用户(程序员)通过配置XML文件或注释等定义的基本逻辑。
它也可以用来解析简单的JavaBean表达式。Runtime(运行时)允许MVEL表达式通过解释执行或者预编译生成字节码后执行。
MVEL是一个基于java语法的表达式,为JAVA语言提供便捷灵活的动态性。MVEL吸收了大量的java语法,但是作为一个表达式语言,还是与java有很多不同之处,比如MVEL像正则表达式一样,有直接支持集合、数组、字符串的操作符。
除了表达式语言以外,MVEL还提供了用来配置和构造字符串的模板语言。
MVEL表达式主要有以下部分内容:
- 属性表达式
- 布尔表达式
- 方法调用
- 变量赋值
- 函数定义
MVEL引入
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.14.Final</version>
</dependency>
MVEL基本语法
属性表达式
基本使用
属性表达式是MVEL最常见的用途之一,通过他MVEL可以用来作为一个高性能易使用的反射优化器。
假设有一个user对象,属性有name,age。
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}```
我们可以通过MVEL获取User 的值,如下:
```java
User user = new User("zhangsan", 123);
Map<String, Object> m = new HashMap<>();
m.put("user",user);
Object o = MVEL.eval("user.name",m);
System.out.println(o);
也可以为user对象设置属性值:
User user = new User("zhangsan", 11);
Map<String, Object> m = new HashMap<>();
m.put("user",user);
MVEL.eval("user.age=22",m);
System.out.println(user.getAge());
with语法
with可以为对象的多个属性赋值。
如为user的name和age同时赋值,语法如下:
with (user){
name='张三',
age=11
}
实例:
User user = new User();
Map<String,Object> m = new HashMap<>();
m.put("user",user);
MVEL.eval("with(user){name='张三',age=30}",m);
System.out.println(user.getName());
布尔表达式
MVEL可以用来表达一个布尔表达式,比如user.name==‘zhangsan’ 。
MVEL的布尔表达式和JAVA一样有优先级,包括通过括号来控制执行顺序。
MVEL布尔表达式的操作符如下:
操作符 | 说明 |
---|---|
== | 等于 |
> | 大于 |
< | 小于 |
!= | 不等于 |
contains | 包含。如果左边的字符串包含右边的字符串内容,返回true |
is/instanceof | 实例类型检查,如果实例类型是右边的类型,返回true |
istrsim | 字符相似 |
sounds | 读音相似 |
~= | 正则表达式测试 |
举几个例子:
大于
Map<String, Integer> m = new HashMap<>();
m.put("a", 100);
Object obj = MVEL.eval("a>10", m);
System.out.println(obj);
读音相似性
String composite = "'foobar' soundslike 'fubar'";
Object obj = MVEL.eval(composite);
System.out.println(obj);
复合表达式
在MVEL中,一段语句中可以写任意多个语句,每个语句以;隔开,最后一个语句没有分号。如以下这个语句:
statement1;statement2;statement3
例:
String com = "a = a+10;a=+11";
Map<String, Object> m = new HashMap<>();
m.put("a", 0);
Object obj = MVEL.eval(com, m);
System.out.println(obj);
返回值
在MVEL中使用了输出最后值原则。
也就是说,虽然MVEL定义了return ,但是不一定用它,因为都一样。
比如下面的脚本:
a=10;b=(a=2)+10;a
这个脚本最后返回的值是a的值,也就是返回最后一个语句的值。
操作符
一元操作符
- new:用来实例化对象,例如:new String(“foo”);
也可以实例化自己定义的对象。例如:User obj = (User) MVEL.eval(“new com.mvel.pojo.User(‘hello’,11)”); - with:对单个对象执行多次操作。
- assert:用一个AssertionError 断言一个值的对错,例:assert foo != null
- isdef:用来判断一个变量在某个范围内是否定义,例:isdef variableName
- !: 布尔取反操作符,例: !true == false
比较运算符
操作符 | 说明 | 示例 |
---|---|---|
== | 比较两个值是否相等,与java的字符串内存地址比较不一样 | ‘foo’==‘foo’ |
!= | 不等于 | |
> | 大于 | |
< | 小于 | |
>= | 大于等于 | |
<= | 小于等于 | |
contains | 包含。如果左边的字符串包含右边的字符串内容,返回true | |
is/instanceof | 实例类型检查,如果实例类型是右边的类型,返回true | |
strsim | 字符相似性 | |
sounds | 读音相似性 | |
~= | 正则表达式测试 |
逻辑运算符
操作符 | 说明 | 示例 |
---|---|---|
&& | 与 | |
|| | 或 | |
or | 用于多个值进行逻辑或运算 |
数字运算符
包括加减乘除(+,-,*,/)等等
其他运算符
-
+,字符串连接运算,如:“foo” +“bar”
-
#,字符连接运算,如:1 # 2返回"12"
-
in,投影整个项目集合,如:(foo in list)
-
=,赋值运算符,如:var = “foobar”
MVEL对列表、数组、map的操作
在MVEL中,可以用非常简单的语法来描述列表,map,数组。
比如:map 在MVEL中的描述
[‘bob’,new User(‘bob’),‘jim’:new User(‘jim’)] 相当于:
Map m = new HashMap();
m.put(‘bob’,new User(‘bob’));
m.put(‘jim’,new User(‘jim’));
列表
用下面的格式描述:[item1,item2,item3] 如[“Jim”,“Bob”,“Tom”]
String expression = "['Jim','Bob','Tom']";
List< String> l = (List<String>) MVEL. eval(expression);
for(String str:l){
System. out.println(str);
}
数组
数组用下面的格式描述:{itm1,itm2,itm3} 如:{“Jim”,“Bob”,“Tom”}
String expression = "{'Jim','Bob','Tom'}";
Object str = MVEL.eval(expression);
String[] newarray = null;
if(str.getClass().isArray()){
System. out.println(String.valueOf(Array. get(str , 0)));
}
Map
String expression = "['Bob' : new com.mvel.pojo.User('Bob'), 'Michael' : new com.mvel.pojo.User('Michael')]";
Map o = (Map ) MVEL.eval(expression);
User u = (User) o.get( "Bob");
System. out.println(u.getName());
数组的强制转换
关于数组,需要知道的一个非常重要的方面是,它可以被强制转换成其它类型的数组,当你声明一个数组时,是不直接指定其类型的,但你可以通过将其传递给一个接收int[]类型参数的方法来指定。如:
foo.someMethod({1,2,3,4});在这种情况下,当MVEL发现目标方法接收的是一个int[],会自动的将{1,2,3,4}转换成int[]类型。
属性访问
在前面的文档中可以了解到MVEL可以访问到对象的属性。比如user.name可以相当于Java中的user.getName()。
Null-Safe Bean Navigation
有时,当你的表达式中会含有null元素时,这时就需要你进行一个为空判断,否则就会发生错误。当你使用null-safe操作符时你可以简化这个操作:user.?manager.name
它相当于:
if (user.manager != null) {
return user.manager.name;
} else {
return null;
}
集合
集合的遍历也可以通过简单的语法来实现:
- List:可以像访问数组一样访问List,如:user[5],这等价与java代码中的user.get(5);
- Map:Map的访问和访问数组也非常相似,不同的是,在访问Map时索引值可以是任意对象,如:user[“foobar”]这等价于java代码中的user.get(“foobar”);
当Map的key是String类型时,还可以使用特殊的方式来访问,如:user.foobar,也就是允许你把map本身看成一个虚拟的对象,来访问其属性
控制流程
if else
MVEL中的if else 和Java中的语法一样。
String compoite = "if (a > 0) {System.out.println('Greater than zero!');}else if (a == -1) {System.out.println('Minus one!');}" +
"else { System.out.println('Something else!');}" ;
String com = "a=a+3;a=a+4;a=a+5";
Map m = new HashMap();
m.put("a", 0);
Object obj = MVEL. eval(com,m );
System. out.println(obj);
三目运算
String expression = "foo>0?'大于0':'小于等于0'" ;
Map m = new HashMap();
m.put("foo", -11);
Object obj = MVEL. eval(expression,m );
System. out.println(obj);
三目运算符还支持嵌套,例如:var > 0 ? “Yes” : (var == -1 ? “Minus One!” : “No”)
迭代
String expression = "foreach (x : 9) { System.out.println(x);} " ;
MVEL. eval(expression);
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String expression = "foreach(el:str){System.out.println(el);}" ;
Map<String, Object> m = new HashMap<String, Object>();
m.put( "str", str);
Object obj = MVEL.eval (expression,m);
目前也支持for关键字。
String expression = "for(i=0;i<9;i++){System.out.println(i)}" ;
MVEL. eval(expression);
do while / do until
do until的意义和do while正好相反
String expression = "do {System.out.println(i);i++;} while (i <10);" ;
Map m = new HashMap();
m.put("i", 1);
Object obj = MVEL.eval (expression,m);
String expression = "do {System.out.println(i);i++;} until (i >10);" ;
Map m = new HashMap();
m.put("i", 1);
Object obj = MVEL.eval (expression,m);
while / until
while (isTrue()) {
doSomething();
}
until (isFalse()) {
doSomething();
}
投影与交集
投影
投影是描述集合的方法之一,通过简单的语法你可以检索到集合中非常复杂的对象模型。
语法:in 比如:(foo in list)(对象属性 in 对象集合)
示例:
User u1 = new User("张三" );
User u2 = new User("李四" );
User u3 = new User("王二" );
List<User> l = new ArrayList<User>();
l.add(u1);
l.add(u2);
l.add(u3);
Map vars = new HashMap();
vars.put("users",l);
String expression = "foo=(name in users);foo";
Object obj = MVEL. eval(expression,vars );
System. out.println(obj);
复杂示例:
public class User {
private String name ;
private Integer age ;
private Parent parent ;
public User(Parent parent) {
this.parent = parent;
}
}
User u1 = new User( new Parent("老大" ));
User u2 = new User(new Parent("老二" ));
User u3 = new User(new Parent("老三" ));
List<User> l = new ArrayList<User>();
l.add(u1);
l.add(u2);
l.add(u3);
Map vars = new HashMap();
vars.put( "users", l);
String expression = "foo=(parent.name in users);foo" ;
Object obj = MVEL. eval(expression, vars );
System. out .println(obj);
投影可以支持嵌套投影,上面的脚本可以写成下面的形式:
foo=(name in (parent in users))
交集
我们可以根据投影这周方式获取交集,比如有一个User对象包含一个集合成员familyMembers,我们可以获取一个家庭成员姓名的集合:
familyMembers =(name in (familyMember in users))
过滤投影
MVEL提供通过if运算符构造的过滤条件来过滤投影,比如获取user集合中name 包含‘张’的用户,就可以使用过滤。
如下面的例子:
User u1 = new User("张依依" );
User u2 = new User("张尔尔" );
User u3 = new User("老三" );
List<User> l = new ArrayList<User>();
l.add(u1);
l.add(u2);
l.add(u3);
Map vars = new HashMap();
vars.put("users", l);
String expression = "($ in users if $.name contains '张')" ;
Object obj = MVEL. eval(expression, vars );
System. out.println(obj);
函数
函数定义
在MVEL中,定义函数用def或function关键字定义。
如:
def hello(){
System.out.println("hello world");
}
function hello(){
System.out.println("hello world");
}
参数与返回值
MVEL中的函数一样可以传参数,返回结果。如下面的函数:
def add(a,b){
a+b;
}
add函数需要传两个参数 a 和b
因为MVEL遵循last-value-out原则,即输出最后值的原则,所以a+b的结果会被返回。也可以使用return关键字强制返回。
函数调用
函数在MVEL脚本文件中定义之后,在JAVA中的调用方法可以看下面的例子:
首先新建一个.el文件写MVEL脚本,test.el:
def add(a,b){
a+b;
}
Java代码如下
//获取脚本文件
File scriptFile = new File("src/com/mvel/test.el" );
VariableResolverFactory resolverFactory = new MapVariableResolverFactory();
//参数
Map map = new HashMap();
map.put("a", 11);
map.put("b", 12);
MVEL. evalFile(scriptFile, ParserContext.create(), map);
Object obj = MVEL. eval("add(a,b);", map);
闭包
Lambda(匿名函数)
threshold = def (x) { x >= 10 ? x : 0 };
result = threshold(13);
System.out.println(result);
关于函数调用的其他内容
MVEL没有发现有自己的内置函数,比如其他的表达式语言如aviator中有获取日期的函数sysdate(),MVEL没有类似的东西。
但是发现MVEL可以调用JAVA中的一些类的方法,比如 Math的方法。