\(^_^)/ 表达式解析器(MVEL)

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

Jeval

在运行时解析计算静态和动态表达式;支持数学,布尔,字符串,函数表达式;支持大部分的数学和布尔运算符;支持自定义函数;支持嵌套函数;支持解析;支持自定义变量解析器;

官网:http://jeval.sourceforge.net

不支持嵌套变量

 

jexel

处理大部分的数学公式和字符串,通过继承接口来实现个人所需的运算;支持自定义变量解析器;该项目一直在更新,最近一次更新是今年十月七号

wiki:http://code.google.com/p/jexel/w/list

不支持变量替换

 

aviator Aviator是直接将表达式编译成Java字节码,交给JVM去执行

支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、位运算符、正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优先级;支持函数调用和自定义函数;支持传入变量,支持类似a.b.c的嵌套变量访问;支持数组的访问;支持正则表达式;

http://www.blogjava.net/killme2008/archive/2010/09/07/331296.html

依赖两外两个jarcommons-beanutils和commons-logging 

 

arity

自定义函数及公式

不支持变量替换

 

Jsci

数学科学计算:包括线性方程组,统计,小波,牛顿力学。

jar包太多,不支持变量替换

 

 

IKExpressoin

支持中文变量及函数名;支持基本运算;函数运算;支持自定义函数;

不支持变量嵌套

 

JSEL

兼容 JavaScript 运算规则的简单的表达式解释引擎,可以通过Map接口,或者JavaBean给出一个变量集合,能后通过表达式从这个集合中抽取变量,再通过表达式逻辑生成你需要的数据;支持函数扩展,操作符别名定义,操作符定义

不支持变量嵌套

 

jep

支持大量的数学运算;其他功能都差不多;支持变量嵌套(顺序执行)

wiki:http://www.singularsys.com/jep/doc/html/variables.html  

 

beanshell

基本运算;变量、函数自定义;支持变量嵌套(顺序执行)  

 

Fel

支持中文变量;支持基本运算;函数运算;支持自定义函数;可调用java方法; 不支持变量嵌套

 

 

在实际项目开发中如果需要解析数学公式,无须再运用解释器模式进行设计,可以直接使用一些第三方解析工具包,它们可以统称为数学表达式解析器(Math Expression Parser, MEP),如Expression4J、Jep、JbcParser、Symja、Math Expression String Parser(MESP)等来取代解释器模式,它们可以方便地解释一些较为复杂的文法,功能强大,且使用简单,效率较好。

      下面简单介绍两个常用的基于Java语言的第三方解析工具包:

 

      (1) Expression4J

      Expression4J是一个基于Java的开源框架,它用于对数学表达式进行操作,是一个数学公式解析器,在Expression4J中可以将数学表达式存储在字符串对象中,如“f(x,b)=2*x-cos(b)”和“g(x,y)=f(y,x)*-2”等。Expression4J是高度定制的,用户可以自定义文法,其主要功能包括实数和复数的基本数学运算,支持基本数学函数(如sin、cos等函数)、复杂函数(如f(x)=2*x+5、g(x)=3*f(x+2)-x等)以及用户使用Java语言自定义的函数和文法,还可以定义函数目录(函数集)、支持XML配置文件等。目前它还不是一个十分成熟的框架,仍在不断完善中。关于Expression4J的更多资料可以参考网站:http://sourceforge.net/projects/expression4j/?source=directory

 

      (2) Jep

      Jep(JavaMathematical Expression Parser)是一个用于解析和求解数学表达式的Java类库。通过使用Jep提供的包,我们可以输入一个以字符串表示的任意数学公式,然后立即对其进行 求解。Jep支持用户自定义变量、常量和自定义函数,同时还包含了大量通用的数学函数和常量。关于Jep的更多资料可以参考网

 

 

groovy

Aviator

BeanShell

EL

FEL

IKExpression

jep

JavaCC

JEval

JEXL

JParsec

JSEL

JXPath

MVEL

Ognl

rhino

SimpleEL

Spads

expression-analyzer

 

 

 

 

 

 

 

 

 

 

 

 

MVEL是一个功能强大的基于Java应用程序的表达式语言。

目前最新的版本是2.0,具有以下特性:

1. 动态JIT优化器。当负载超过一个确保代码产生的阈值时,选择性地产生字节代码,这大大减少了内存的使用量。

新的静态类型检查和属性支持,允许集成类型安全表达。

2. 错误报告的改善。包括行和列的错误信息。

3. 新的脚本语言特征。MVEL2.0 包含函数定义,如:闭包,lambda定义,标准循环构造(for, while, do-while, do-until…),空值安全导航操作,内联with-context运营 ,易变的(isdef)的测试运营等等。

4. 改进的集成功能。迎合主流的需求,MVEL2.0支持基础类型的个性化属性处理器,集成到JIT中。

5. 更快的模板引擎,支持线性模板定义,宏定义和个性化标记定义。

6. 新的交互式shell(MVELSH)。

 

MVEL是一种用于Java应用程序,类似于OGNL的表达式语言。

MVEL不仅非常小和敏捷,而且它的语法易于阅读与EL 或OGNL比起来更像Java。

比如静态方法和属性的引用方式与Java一样,赋值也非常像Java

 

MVEL为 MVFLEX Expression Language(MVFLEX表达式语言)的缩写,它是一种动态/静态的可嵌入的表达式语言和为Java平台提供Runtime(运行时)的语言。

最初是作为一个应用程序框架实用程序的语言开始,该项目现已发展完全独立。

MVEL通常用于执行用户(程序员)通过配置XML文件或注释等定义的基本逻辑。

它也可以用来解析简单的JavaBean表达式。Runtime(运行时)允许MVEL表达式通过解释执行或者预编译生成字节码后执行。

 

 

 

MVEL最初是Mike Brock的Valhalla项目的表达计算器。Valhalla本身是一个早期的Seam,就像为自动化“out of the box”web应用而生的框架,虽然Valhalla现在是休眠状态,但是MVEL仍旧作为活跃的开发项目向前发展。通常,我们会将MVEL同OGNL、JEXL、JUEL这样的项目作对比;不论是性能、特性还是易用性,尤其是集成方面,MVEL都已经远远超过那些项目。MVEL还没有尝试另一种JVM语言,但是开始关注解决嵌入式脚本的问题。

由于内存限制或者沙盒不能用字节码生成的约束环境中,MVEL则十分理想。取代尝试再创在Java,相反,MVEL目的在于为Java程序员提供一种类似的语法,同时也为短的和简明表达式添加了语法糖衣。

MVEL是Drools规则引擎的一部分,很多密封集成点式同Drools团队一同开发的。那会其他脚本语言可以再次进行复查,但是也显示了一下的问题:

缺少可选类型安全

集成不良,通常通过映射填入内容。没有字节码不能运作用字节码生成编译时间慢,还增加了可扩展性问题;不用字节码生成运行时执行非常慢

内存消耗过大

Jar巨大/依赖规模

 

 

 

 

 

 

官网

http://mvel.codehaus.org/

 

下载:

http://repository.codehaus.org/org/mvel/mvel2/2.2.0.Final/

https://github.com/mvel/mvel

 

OGNL 2.7.2 、JEXL 1.1、JUEL 2.1.0、Groovy 1.5.7、MVEL 2.0性能测试 

http://www.iteye.com/topic/361794

 

mvel语法指南

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

http://blog.csdn.net/fhm727/article/details/6543152

 

Ognl/MVEL/Aviator/JSEL 四种表达式引擎执行效率对比

http://jindw.iteye.com/blog/732354

 

OGNL & MVEL &java调用 性能误解

http://caoyaojun1988-163-com.iteye.com/blog/2089726

 

 

 

虽然mvel吸收了大量的java语法,但作为一个表达式语言,还是有着很多重要的不同之处,以达到更高的效率,比如:mvel像正则表达式一样,有直接支持集合、数组和字符串匹配的操作符。

除了表达式语言外,mvel还提供了用来配置和构造字符串的模板语言。

mvel2.x表达式包含以下部分的内容:属性表达式,布尔表达式,方法调用,变量赋值,函数定义。

 

 

 

 

一、基本语法

MVEL 是一种基于java语法,但又有着显著不同的表达式语言。与java不同,MVEL是动态类型(带有可选分类),也就是说在源文件中是没有类型限制的。一条MVRL表达式,简单的可以是单个标识符,复杂的则可能是一个充满了方法调用和内部集合创建的庞大的布尔表达式。

 

1、简单的属性表单式:user.name

在这个表达式中,我们只是有一个标识符(user.name),这就是我们所说的MVEL的AA级属性表达式,该表达式的唯一目的是获取一个变量或上下文对象的属性。属性表达式是MVEL的最常见的用途之一,通过它,MVEL可以用来作为一个高性能,易使用的反射优化器。

 

2、布尔表达式:

MVEl也可以用来表示一个布尔表达式,如:user.name == 'John Doe'.像java一样,MVEL支持所有优先级规则,包括通过括号来控制执行顺序,如:(user.name == 'John Doe') && ((x * 2) - 1) > 20

 

3、复合语句表达式:

在一段脚本里,你可以写任意多个语句,但注意要用分号来作为每个语句的结束符,只有一个语句时或最后一个语句时除外。例如:statement1; statement2; statement3 注意最后一个语句没有分号。另外,换行不能替代分号来作为一个语句的结束标识。

 

4、返回值:

比如,MVEL使用了输出最后值原则,也就是说,尽管MVEL定义了return关键字,但却没有必要用它。例如;

a = 10;

b = (a = a * 2) + 10;

a;

这段脚本将最后一个表达式的值a作为自己的值返回,功能上它与下面这段脚本等价:a = 10;

b = (a = a * 2) + 10;

return a;

 

 

 

 

二、操作符

下面列出了MVEL中所有的操作符:

 

一元操作符:

new,用来实例化对象,例:new String("foo")

with,对单个对象执行多个操作,例:with (value) { name = 'Foo', age = 18, sex = Sex.FEMALE }

assert,用一个AssertionError 断言一个值的对错,例:assert foo != null

isdef,用来判断一个变量在某个范围内是否定义,例:isdef variableName

!,布尔取反操作符,例: !true == false

比较运算符;

常见的比较运算符==,!= ,>,<,>=,<=等不再赘述

contains,判断左边的值是否包含右边的值,如: var contains "Foo"

is/instance of ,判断左边的值是否是右边的类的实例,如:var instanceof Integer

strsim,比较两个字符串的相似度,返回一个百分数,如; "foobie" strsim "foobar" 

soundslike,比较两个字符串的发音,如:"foobar" soundslike "fubar"

 

逻辑运算符:

&&,||略

or,用于多个值间进行逻辑或运算,如:foo or bar or barfoo or 'N/A' 

~=,正则表达式匹配符,如:foo ~= '[a-z].+'

 

按位运算符:&,|,^等

 

数学运算符:+,-,*,/等

 

其它运算符:

+,字符串连接运算,如:"foo" +"bar"

#,字符连接运算,如:1 # 2返回"12"

in,投影整个项目集合,如:(foo in list)

=,赋值运算符,如:var = "foobar"

 

 

 

 

三、值判断

在MVEL中所有的判断是否相等 都是对值的判断,而没有对引用的判断,因此表达式foo == 'bar' 等价于java中的foo.equals("bar").

 

1、判断值是否为emptiness(需要解释emptiness)

MVEL提供了一个特殊的字符来表示值为emptiness的情况,叫作empty,如:foo == empty,若foo满足emptiness的任何条件,这个表达式值都为true

 

2、为null测试

MVEL中,null和nil都可以用来表示一个空值,如:foo == null ; foo == nil;

 

3、强制转换

当两个不同类型且没有可比性的值进行比较时,需要将左边的值强制转换成右边的值的类型时,MVEL会应用类型强制转换系统,反之亦然。如:"123" == 123;这个表达式的值为true,因为为了执行比较,强制类型转换系统会隐式的将数字123转换成字符串。

 

 

 

 

四、列表、map和数组

在MVEL中你可以使用非常简单的语法来描述列表、map、数组,且看下面的例子:

['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']);

 

1、列表

列表用下面的格式来描述:[item1, item2, ...],如:["Jim", "Bob", "Smith"]

 

2、map

map的描述格式:[key1 : value1, key2: value2, ...],如:["Foo" : "Bar", "Bar" : "Foo"]

 

3、数组,格式:{item1, item2, ...},如:{"Jim", "Bob", "Smith"}

 

4、数组的强制转换

关于数组,需要知道的一个非常重要的方面是,它可以被强制转换成其它类型的数组,当你声明一个数组时,是不直接指定其类型的,但你可以通过将其传递给一个接收int[]类型参数的方法来指定。如:

foo.someMethod({1,2,3,4});在这种情况下,当MVEL发现目标方法接收的是一个int[],会自动的将{1,2,3,4}转换成int[]类型。

 

 

 

 

五、属性访问

对于bean属性的访问,在 Groovy, OGNL, EL等脚本语言的bean 属性表达式中已经形成了一个相对比较稳定的方式,MVEL也采用了这一方式。和其它语言必须通过底层的方法来控制权限不同的是,MVEL提供了一套独立的,统一的语法来访问属性,静态字段还有map。

 

1、bean properties

大多数java开发者都熟悉 getter/setter 模式,并在java对象中用它来封装属性的访问权限。例如,你可能会通过下面的方式访问一个对象的属性:

user.getManager().getName();

简便起见,在MVEL中你也可以用下面的表达式来访问:

user.manager.name

注意:当一个对象中的字段的作用域是public时,MVEL仍然倾向于通过get方法来访问其属性。

 

2、Null-Safe Bean Navigation

有时,当你的表达式中会含有null元素时,这时就需要你进行一个为空判断,否则就会发生错误。当你使用null-safe操作符时你可以简化这个操作:user.?manager.name

它相当于:if (user.manager != null) { return user.manager.name; } else { return null; }

 

3、集合

集合的遍历也可以通过简单的语法来实现:

List:可以像访问数组一样访问List,如:user[5],这等价与java代码中的user.get(5);

Map:Map的访问和访问数组也非常相似,不同的是,在访问Map时索引值可以是任意对象,如:user["foobar"]

这等价于java代码中的user.get("foobar");当Map的key是String类型时,还可以使用特殊的方式来访问,如:user.foobar,也就是允许你把map本身看成一个虚拟的对象,来访问其属性

 

4、字符串作数组

为了能使用属性的索引(迭代也是如此),所有的字符串都可以看成是一个数组,在MVEL中你可以用下面的方式来获取一个字符串变量的第一个字符:

foo = "My String";

foo[0]; // returns 'M'

 

 

 

 

六、常量

在脚本语言中,一段文字用来代表一个固定的值 

 

1、字符串常量:

字符串常量可以用一对单引号或一对双引号来界定。如:

"This is a string literal"

'This is also string literal'

字符串中的特殊字符:

// - 代表一个反斜杠.

/n - 换行符

/r -回车符

/u#### - Unicode 字符(如: /uAE00)

/### - Octal字符(如: /73)

 

2、数字常量 

 整数可以表示为十进制(基数为10),8进制(基数为8),或十六进制(基数为16)。

一个十进制数字,不从零开始(相对于8进制、16进制而言),可以表示任意数,如:125

一个八进制数,以0为前缀,后面跟着0到7内的数字

一个十六进制,以0X为前缀,后面可以跟着0-9,A-F范围内的数字

 

3、浮点型常量

如:10.503 // a double

94.92d // a double

14.5f // a float

 

4、BigInteger 和 BigDecimal型常量

如:104.39484B // BigDecimal

8.4I // BigInteger

 

5、布尔型常量

布尔型常量用保留关键字true和false来表示。

 

6、空常量

用null或nil来表示

 

 

 

 

七、程序控制

事实上,MVEL的强大已经超出了简单的表达式。它提供了一系列的程序控制操作符来提高你的脚本操作,

 

1、If-Then-Else

MVEL提供了完整的C/Java式的if-then-else块,如:

if (var > 0) {

   System.out.println("Greater than zero!");

}

else if (var == -1) { 

   System.out.println("Minus one!");

}

else { 

   System.out.println("Something else!");

}

 

2、三元声明

其实就是Java中的条件表达式,如:var > 0 ? "Yes" : "No";

可以嵌套,如:var > 0 ? "Yes" : (var == -1 ? "Minus One!" : "No")

 

3、foreach

MVEL的强大特性之一就是其Foreach操作符,在功能和语法上,他都类似于java1.5中的for each操作符,它接收用冒号隔开的两个参数,

第一个是当前元素的一个域变量,而第二个是要迭代的集合或数组。如下所示:

count = 0;

foreach (name : people) {

   count++;

   System.out.println("Person #" + count + ":" + name);

}

System.out.println("Total people: " + count);

因为MVEL将字符串视作一个可以迭代的对象,所以你可以用foreach语句来迭代一个字符串(一个字符接一个字符的):

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]

你也可以利用MVEL进行计数(从1开始):

foreach (x : 9) { 

   System.out.print(x);

输出:123456789

注意:像java5.0一样,在MVEL2.0中,可以将foreach简化成关键字for来使用,如:

for (item : collection) { ... }

 

4、for循环

MVEL实现了标准的C语言的for循环:

for (int i =0; i < 100; i++) { 

   System.out.println(i);

}

 

5、Do While,Do Until

和java中的意义一样,MVEL也实现了Do While,Do Until,While和Until意义正好相反。

do { 

   x = something();

while (x != null);

在语义上相当于:

do {

   x = something();

}

until (x == null);

 

7、While, Until

MVEL中实现了标准的While,并添加了一个与之相反的Until

while (isTrue()) {

   doSomething();

}

或者是:

until (isFalse()) {

   doSomething();

}

 

 

 

 

八、投影和交集

简而言之,投影(也叫列表解析)是描述集合的方法之一,通过非常简单的语法,你可以检索集合中非常复杂的对象模型。假设有一个User对象的

集合,每一个User都有一个Parent。现在你想获得集合users中的所有parent的name的列表(假设Parent中有字段name),你可以这样来写:

parentNames = (parent.name in users);

你甚至可以执行嵌入式操作,设想,User对象有个集合成员叫做familyMembers,现在我们想获得一个所有家庭成员姓名的集合:

familyMembers = (name in (familyMembers in users));

你可以通过用if运算符构造的条件来过滤投影:(doSomeMethod() in listOfThings if $.shouldBeRun())

其中的$是一个占位符,用来表示被过滤的元素。其实,它就是一个存在于投影上下文内部的普通变量,你还可以用它返回一个列表的投影中的当前

元素:

($ 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允许你对表达式中的变量进行赋值,以便在运行时获取,或在表达式内部使用。因为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";

 

 

 

 

十、

EL可以使用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支持通过宏定义来用外部的可扩展资源来代替一个标记,这是它的一项基本的功能。这一功能被用来创建封装了解释器的特殊关键字。

一个宏可以由任意多个合法的标识符组成,如:modify

考虑下面的代码:

modify (obj) { value = 'foo' };

这不是一个合法的MVEL表达式,因为毕竟在MVEL中没有modify这个关键字。然而,在JBoss Drools 中会用这一功能通过用字符串

@Modify with替代modify标识符来实现结构变化监听器,

 

1、org.mvel.Macro 接口:

public interface Macro {

    public String doMacro();

}

这个接口非常简单,执行时方法 doMacro() 会返回一个字符串来代替原有的标记,例如:

Macro modifyMacro = new Macro() {

     public String doMacro() {

         return "@Modify with";

     }

}

 

2、使用MacroProcessor

MacroProcessor是一个轻量级的快速文本转换器,它使用定义的宏来替代所有相对应的标识符。定义的宏存储在一个Map中,需要匹配的标识符作为

键,宏的实例作为值,然后传递给MacroProcessor,如:

Map<String, Macro> myMacros = new HashMap<String, Macro>();

// Add modifyMacro to the Map

myMacros.put("modify", modifyMacro);

// Create the macro processor

MacroProcessor macroProcessor = new MacroProcessor();

// Add the macro map to the macro processor

macroProcessor.setMacros(myMacros);

// Now we pre-parse our expression

String parsedExpression = macroProcessor.parse(expression);

返回的字符串就会直接传递给MVEL的编译器。

 

 

 

 

十三、拦截器

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处理类的引用的方式。

Java表达式注入漏洞深度解析:从SpEL、OGNL原理到代码审计实战 表达式注入是一种常见的安全漏洞,其本质在于程序将用户输入的数据错误地当作代码逻辑执行,导致业务逻辑被篡改。这类漏洞通常发生在集成了动态表达式引擎(如SpEL、OGNL)的应用中,攻击者通过构造恶意表达式,可引发逻辑错误、数据泄露甚至远程代码执行。从技术原理看,表达式引擎通过解析、求值和上下文绑定三个步骤工作,而漏洞往往源于过于强大的默认上下文配置与不可信输入的结合。在微服务与动态配置普及的当下,掌握表达式注入的审计与防御已成为Java开发者与安全工程师的必备技能。本文将以SpEL和OGNL等主流引擎为例,深 阅读详情

相关推荐

表达式解析器MVEL的了解

MVEL(MVFLEX Expression Language)是一种基于Java的表达式语言,它允许在运行时解析和执行表达式MVEL 最初是为了提供一个更强大、更易于使用的替代品来执行 Java 的字符串操作和表达式求值而创建的。MVEL 的语法类似于 Java,但更加简洁和灵活,使得编写和阅读表达式更加容易。MVEL 支持各种操作符、条件语句、循环语句等,可以完成复杂的表达式求值和字符串操作。MVEL 支持在运行时动态解析和执行表达式,这意味着您可以在不重新编译代码的情况下修改和执行表达式

没啥可说的,一切都看博客吧 2288

MVEL 2.0表达式语言

MVEL 2.0表达式语言MVEL 2.0表达式语言MVEL 2.0表达式语言

MVEL快速入门—MVEL基础语法讲解(一)

概述 MVEL是从英文翻译而来的,MVFLEX表达式语言是Java平台的动态/静态混合类型的运行时可嵌入表达式语言。该项目最初是作为应用程序框架的实用语言开始的,现在已完全独立开发。MVEL通常用于通过XML文件或注释等配置将基本逻辑公开给最终用户和程序员。它也可以用来解析简单的JavaBean表达式。 在一些项目中我们需要对一些场景进行过滤并允许用户配置的时候,通常会采用MVEL表达式,比如:指定条件下更新数据;指定条件下采集数据等。 相关文章 ????Java解析执行MVEL表达式 .

北鹤M的代码手账 3871

MVEL语法指南

MVEL全称为:MVFLEX Expression Language,是用来计算Java语法所编写的表达式值的表达式语言。MVEL的语法很大程度上受到Java语法的启发,但为了使表达式语法更高效,还是有一些基本差异,例如可以像正则表达式一样直接支持集合、数组和字符串匹配的运算。 除了表达式语言之外,MVEL还用作配置和字符串构造的模板语言。这里还有一个关于MVEL介绍信息的wiki页面是:htt...

liinux-Talk is cheap,show me the code. 7960

MVEL简介及快速使用

MVEL为MVFLEX Expression Language(MVFLEX表达式语言)的缩写,它是一种动态/静态的可嵌入的表达式语言和为Java平台提供Runtime(运行时)的语言。最初是作为一个应用程序框架实用程序的语言开始,该项目现已发展完全独立。MVEL通常用于执行用户(程序员)通过配置XML文件或注释等定义的基本逻辑。它也可以用来解析简单的JavaBean表达式。Runtime(运行时)允许MVEL表达式通过解释执行或者预编译生成字节码后执行。 MVEL是一个基于java语法的表达式,为JA.

lixinkuan的博客 1万+

\(^_^)/ 表达式解析器(MVEL)

Jeval 在运行时解析计算静态和动态表达式;支持数学,布尔,字符串,函数表达式;支持大部分的数学和布尔运算符;支持自定义函数;支持嵌套函数;支持解析;支持自定义变量解析器; 官网:http://jeval.sourceforge.net 不支持嵌套变量   jexel 处理大部分的数学公式和字符串,通过继承接口来实现个人所需的运算;支持自定义变量解析器;该项目一直在更新,最近一次...

yanguz的专栏 2639

表达式引擎JSEL介绍

表达式引擎的定义这里我就不说了,先假设大家都知道^_^ 在[url=http://code.google.com/p/lite/wiki/JSEL]JSEL[/url]之前也出现过不少其他类似产品,包括老牌的Ognl(老到网站都找不到了),新来的[url=http://mvel.codehaus.org/Getting+Started+for+2.0]MVEL[/url],还有我们国产的...

专栏已毁弃,暂时迁移至:http://jindw.javaeye.com 545

java jsel_表达式引擎JSEL介绍

表达式引擎的定义这里我就不说了,先假设大家都知道^_^在JSEL之前也出现过不少其他类似产品,包括老牌的Ognl(老到网站都找不到了),新来的MVEL,还有我们国产的Aviator,IKExpression居然有了这么多可用的实现,那么JSEL的亮点又在那里呢?基于ECMA262标准的子集JSEL是一个兼容 JavaScript 运算规则的简单的表达式解释引擎。支持ECMA标准的运算符、函数库并内...

weixin_40002009的博客 1623

关于 Spring SpEL 表达式

SpEL(Spring表达式语言)是Spring框架提供的一种功能强大的表达式解析工具,支持运行时动态计算表达式。它具备访问对象属性、方法调用、逻辑运算、集合操作等能力,广泛应用于Spring配置、注解(如@Value、@Cacheable)、权限控制等场景。SpEL通过解析表达式生成AST语法树,结合上下文环境进行求值。其特性包括运算符支持、条件表达式、对象创建、Bean引用等,还提供安全访问、集合过滤/投影等高级功能。Spring 4.1+支持编译优化提升性能,但需注意控制表达式复杂度、防范安全风险,并

四楼没电梯的专栏 632

表达式注入漏洞深度解析:从原理到防御实践

表达式注入是一种代码注入攻击,其核心原理在于应用程序将用户可控的字符串作为代码指令,交由表达式引擎(如Spring EL、OGNL、SpEL)动态执行。攻击者通过精心构造的输入,可绕过预期逻辑,利用引擎的强大功能执行任意命令,例如访问文件系统、执行系统命令或窃取敏感数据,从而完全控制应用。这类漏洞的技术价值在于其隐蔽性高,常潜伏于业务逻辑层,利用的是应用自身提供的合法功能,危害性极大。在应用场景上,它广泛存在于需要动态解析用户输入的环节,如模板渲染、规则引擎、配置中心以及工作流条件判断等。本文以典型的CVE

weixin_30596735的博客 345

Spring Expression Language (SpEL) 使用教程

本文介绍了Spring表达式语言(SpEL)的核心概念和使用方法。主要内容包括: SpEL概述:作为Spring框架3.0引入的表达式语言,用于运行时查询和操作对象图,支持属性访问、逻辑运算等功能。 表达式生命周期:从字符串解析为AST语法树,在EvaluationContext中求值,并可编译为字节码提升性能。 基础语法:支持字符串、数值、布尔等字面量,提供算术、比较、逻辑、三元等运算符。 快速入门示例:演示了基本表达式求值和对象属性访问两种典型用法。 核心特性:包括Elvis运算符简化null判断,正则

xhmico的博客 767

Java低代码平台高危漏洞解析与加固实战:应对APT攻击的三大补丁方案

在软件开发领域,低代码平台通过可视化组件和模型驱动,显著提升了应用构建效率,其核心原理在于对复杂技术逻辑的抽象与封装。然而,这种高度抽象在带来便捷的同时,也引入了新的安全风险,尤其是当平台依赖的底层技术组件存在缺陷时,其安全价值便会受到严峻挑战。具体而言,表达式注入和不安全的反序列化是Java生态中常见的高危漏洞类型,它们允许攻击者在特定场景下执行任意代码,从而完全控制应用服务器。本文聚焦于Java低代码平台,深入剖析了由这些通用漏洞衍生的实际攻击路径,并提供了针对性的工程实践加固方案,旨在帮助开发与安全团

weixin_33738982的博客 352

MySQL自定义函数模板实现动态字符串与加密的配置化方案

在数据库应用开发中,动态字符串生成与数据加密是常见的核心需求,例如生成包含日期和序列号的订单号,或根据业务规则采用不同盐值进行密码加密。传统硬编码方式存在代码冗长、维护困难的问题。通过引入模板化思想,可以将字符串生成规则和加密逻辑抽象为可配置的模板,实现逻辑与代码的解耦。其技术原理在于设计一套模板语法,通过占位符解析引擎动态替换变量,并集成序列生成器与加密函数。这种方案的技术价值在于提升代码的灵活性、可维护性和统一性,特别适合中后台系统、SaaS平台等需要高度配置化的场景。本文以MySQL为例,详细阐述了如

weixin_30703911的博客 376

SpringBoot MVEL 表达式

在动态规则执行和条件匹配方面,MVEL 是一个出色的选择,因为它允许你在运行时定义和修改规则,并立即处理这些规则。这样做的好处是,你可以在运行时根据需要更改或增加规则,而无需修改并重新编译源代码。这些规则不是在代码中硬编码的,而是可以在运行时更改的,这给了你更大的灵活性。对于更复杂的情况,您可以将一些自定义的函数或其他复杂对象添加到变量映射中,然后在 MVEL 表达式中使用它们。以上假设你的 表达式是正确的,如果它没有匹配到,可能是因为你的表达式格式不正确,或你的flight 对象没有正确地设置。

pengyi123的博客 4968

MVEL表达式入门

mvel初步

shihunyewu的博客 1864

【Java工具类】学会MVEL2.0,表达式解析再不怕

MVEL为 `MVFLEX Expression Language`(MVFLEX表达式语言)的缩写,是一种基于`Java语法`,但又有着显著不同的表达式语言。与Java不同的是,MVEL是一种`动态/静态可嵌入`的表达式语言,意味着源代码中不需要`类型限定`。 - 简单说是一种`表达式解析器。`我们可以自己写一些表达式,交给mvel进行解析计算,得到这个表达式计算的值。

墩墩分墩 8332

表达式引擎Mvel详解与实战

表达式引擎Mvel详解与实战

fuliyingIT的博客 6460

MVEL表达式

MVEL(MVFLEX Expression Language)是一种基于 Java 平台的表达式语言,用于在运行时进行动态计算和求值。MVEL 允许在运行时动态地执行表达式,而不需要事先进行编译。它提供了一种简洁而强大的语法,支持各种运算符、函数调用、变量引用、条件语句和循环语句等功能。但是这样可能有被恶意执行代码的风险,确保不会执行恶意代码的关键在于对用户输入进行充分的验证和过滤,以确保输入的内容是安全的。js使用样例:(可以定义一个输入框,mvel表达式由用户输入,可以动态构建表达式并计算)

qq_43577380的博客 1399

Mvel 2.0 使用指南一 基础

MVEL在很大程度上受到Java语法的启发,作为一个表达式语言,也有一些根本的区别,旨在更高的效率,例如:直接支持集合、数组和字符串匹配等操作以及正则表达式MVEL用于执行使用Java语法编写的表达式。除了表达语言之外,MVEL还可用作配置和字符串构造的模板语言。MVEL2.x表达式包含以下部分的内容:属性表达式布尔表达式方法调用变量赋值函数定义1. 基本语法MVEL 是一种基于Java语法,

SmartSi 3万+
上一篇: IKExpressionV2.1.2快速入门
下一篇: postgresql双机热备(流复制)及主备切换
DemonHunter211
博客等级 码龄12年 2852粉丝 3055原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值