当前位置: 首页 > 工具软件 > MVEL > 使用案例 >

java mvel_你的Java开发工具包有没有MVEL?

田兴朝
2023-12-01

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巨大/依赖规模

属性访问函数和数据结构

MVEL支持类javascript语法,这是我们熟悉并喜欢的表达语言,支持简化的属性访问函数,内联映射以及列表:

以下是引用片段:

[1, 2, 3, 4] // array

["key1" : 1, "key2" : 3 ] // map

person.name // simple property accessor

person.pets["rover"].age // map accessor

person.pets["rover"].?age // null safe accessor

使用“with”声明,并内联函数构造器,下面是等价功能:以下是引用片段:

with ( new Person() ) {

name = "Bobba Fet", age = "50"

}

new Person().{

name = "Bobba Fet", age = "50"

}

也可以对复杂数据结构创建使用内联映射和列表语法:以下是引用片段:

[ people : [

new Person().{ name = "Bobba Fet" },

new Person().{ name = "Darth Vader" }

],

places : [

new Place().{ name = "Death Star" }

]

]

在MVEL中就像其他语言一样返回值,默认的是最后一个值。此外,你也可以使用“return”关键词从程序中手动返回。以下是引用片段:

if (val) { “foo”}

else

{ “bar”};

Is the same as:

if (val) { return “foo” }

else

{ return “bar” };

这个例子中通过关键词“room”和“doors”返回一个Map,Door参照了两个room Map中以前创建的实例

以下是引用片段:

rooms = [

"basement" : new Room("basement"),

"lounge" : new Room("kitchen")

];

// return is implicit,

// but can be explicitly added if preferred

doors = [

new Door(

rooms["kitchen"], rooms["basement"]

)

];

[ "rooms" : room, "doors" : doors ];

外部变量词典

MVEL可以使用Map计算给定的表达,提供一个可用变量词典:

以下是引用片段:

Map vars = new HashMap();

vars.put( "person", p );

// p is a previous created instance

//p.pets references a Map.

Dog dog =

( Dog ) MVEL.eval( "person.pets['rover']", vars );

上面所说的将会使用MVEL的“interpreted”模式,快速单程编译和执行String。它也能为更快的执行进行预编译。

MVEL通过性能优化的分析器开发,保持了jar大小,而且意味着MVEL不要求任何附加的外部依赖。Jar大概700kb左右,包括调试信息。编译声明仍旧会被解读,使用一种优化的内部执行图表。对于性能敏感的代码,MVEL的优化程序可以发出字节码来加速域和方法的访问。

编译例子:

以下是引用片段:

ExecutableStatement stmt =

MVEL.compileExpression( "person.pets['rover']" );

MVEL.executeExpression( stmt, vars );

内容对象可用来执行脚本,而不是提供对象。注意下面的例子中,我们可以直接访问“pet的属性”,这对于Person的方法也同样有效:

以下是引用片段:

Dog dog =

( Dog ) MVEL.eval( "pets['rover']", p );

内容对象和变量词典可以共同使用:

以下是引用片段:

vars.put( "otherPet", patch )

// patch is another pet

MVEL.eval(

"pets['rover'].age == otherPet.age", p, vars

);

输入声明可行,“*”也可以成套引进使用:

以下是引用片段:

"import org.domain.Dog; pets['rover'] == new Dog('patch', 7)"

类输入可以通过可重用的ParserConfiguration自动添加,如果这个输入可以被编译中的大多数表达所共享,ParserConfiguration相当不错。以下是引用片段:

ParserConfiguration pconf =

new ParserConfiguration();

pconf.addImport( "Dog", Dog.class );

ParserContext pctx = new ParserContext( pconf );

MVEL.compileExpression(

"pets['rover'] == new Dog('patch', 7)", pctx

);

输入信息和输入安全

ParserContext在外部变量编译时间可随意用于提供输入信息。在哪里提供输入信息,编译器就可以更好的假设,结果就是更快地执行性能。局部变量部署支持类型推断,如果他们可以推断出类型就可以优化。以下是引用片段:

ParserContext pctx = new ParserContext( );

pctx.addInput( “p”, Person.class );

MVEL.compileExpression( "p.pets['rover']", pctx );

此外,MVEL已经能够以非类型安全的方式动态执行,这一点类似其他的表达语言,像OGNL或者JEXL。但是编译要确保所有输入安全,使用可选的强键入。如果输入信息不可用或者不能推断,编译就会失败。

以下是引用片段:

ParserContext pctx = ParserContext.create()

.stronglyTyped()

.withInput(“p”, Person.class);

MVEL.compileExpression( "p.pets['rover']", pctx );

Setting the type of the context object can be done via the 'this' input:

ParserContext pctx = ParserContext.create()

.stronglyTyped()

.withInput( “this”, Person.class );

MVEL.compileExpression( "this.toString()", pctx );

完全的输入推断是为局部变量提供的,所以输入声明或者类型转换的支持是可选的。MVEL也可以从可用的泛型中推断输入信息:以下是引用片段:

public static class Person {

private Map pets;

...

}

...

ParserContext pctx = ParserContext.create()

.stronglyTyped();

.withInput( “person”, Person.class );

//Both the following MVEL statements are valid and type safe:

MVEL.compileExpression( "Pet rover = (Pet) person[“rover”];n”+

“return rover.age;", pctx );

MVEL.compileExpression( "rover = person[“rover”];n”+

“return rover.age;", pctx );

MVEL比较有意思的一项性能是提供分析表达式的功能,而且可以告诉你外部输入,也就是变量不是本地分配的。也可以用于为局部变量报告推断输入。确定了外部输入时,强键入就不能使用,而且输入的内容将会被作为java.lang.Object处理。以下是引用片段:

ParserContext pctx = ParserContext.create();

MVEL.compileExpression( "person.pets['rover']", pctx );

// Map returns [“person” : Object.class]

Map inputs = pctx.getInputs();

从局部变量获取输入:

以下是引用片段:

ParserContext pctx = ParserContext.create()

.stronglyTyped()

.withInput( “person”, Person.class );

MVEL.compileExpression( "pet = person.pets['rover']", pctx );

// Map returns [“pet” : Pet.class]

Map inputs = pctx.getVariables();

索引变量词典

对外部变量词典使用Map是嵌入式脚本语言最常用的方式,但是并不是性能的最优化方式。这回消耗过多的内存,而且每一次读或者写就是一个Map put/get。MVEL的最新版本通过配置属性“indexAllocation”,引入了预计算索引变量,所以读和写变量和访问矩阵一样快速,在内存消耗上不会比矩阵匹配的变量长度需求多。索引也可以用于局部变量,确保高性能。以下是引用片段:

ParserContext pctx = new ParserContext( )

.stronglyTyped()

.withInput(“person”, Person.class)

.withInput(“otherPet”, Person.class)

String[] varNames = new String[] { “person”, “otherPet” };

pctx.addIndexedInput(varNames);

pctx.setIndexAllocation(true);

String exp = “person.pets['rover'].age == otherPet.age”;

SharedVariableSpaceModel model =

VariableSpaceCompiler.compileShared(expr, ctx);

ExecutableStatement stmt = MVEL.compileExpression( expr, pctx );

// Notice the values order must

// match the indexedInput variable name order

Object[] values = new Object[] { person, patch };

MVEL.executeExpression(stmt, model.createFactory(values) );

增强功能可选字节码生成

高度优化实时编译器

编写优化

逃逸分析和冗余优化

总结

到现在你应该可以确信,作为Java开发者你不能把MVEL拿出你的工具盒了吧,MVEL填补了完成编程环境之间独特的代沟,像Groovy和Scala和表达语言OGNL和JUEL。就算全世界在讨论Gavin King的Ceylon项目的各种优点,他们错过了Jboss的秘密武器。

 类似资料: