来源:org.apache.commons.jexl3 (Apache Commons JEXL 3.2.1 API)
JEXL 是一个表达式语言引擎,可以在应用程序或框架中实现动态和脚本功能。
一个简单的例子:
// 创建 JexlEngine (可以重用)
JexlEngine jexl = new JexlBuilder().create();
// 创建一个表达式对象 'car.getEngine().checkStatus()':
String jexlExp = "car.engine.checkStatus()";
Expression e = jexl.createExpression( jexlExp );
//car 作为一个入参管理
Car car = theCarThatWeHandle;
// 创建一个contenxt,并且添加数据
JexlContext jc = new MapContext();
jc.set("car", car );
// 现在计算表达式,得到结果
Object o = e.evaluate(jc);
使用JEXL
Jexl API 由三个级别组成,满足不同的功能需求:
- setter、getter、方法和构造函数的动态调用;
- JEXL表达式的脚本表达式
- 类似JSP/JSF的表达式,称为JXLT表达式
注意事项
公共的API位于以下两个包中:
- org.apache.commons.jexl3
- org.apache.commons.jexl3.introspection
以下包这些仅用于扩展JEXL。它们的类和方法不能保证在后续版本中保持兼容。如果你认为你需要直接使用他们的一些功能或方法,最好先通过邮件列表与社区联系。
- org.apache.commons.jexl3.parser
- org.apache.commons.jexl3.scripting
- org.apache.commons.jexl3.internal
- org.apache.commons.jexl3.internal.introspection
动态调用
这些功能与BeanUtils中的核心级实用程序非常接近。对于基本的动态属性操作和方法调用,可以使用以下一组方法:
- JexlEngine.newInstance(java.lang.Class<? extends T>, java.lang.Object...)
- JexlEngine.setProperty(java.lang.Object, java.lang.String, java.lang.Object)
- JexlEngine.getProperty(java.lang.Object, java.lang.String)
- JexlEngine.invokeMethod(java.lang.Object, java.lang.String, java.lang.Object...)
下面的示例说明了它们的用法:
// test outer class
public static class Froboz {
int value;
public Froboz(int v) { value = v; }
public void setValue(int v) { value = v; }
public int getValue() { return value; }
}
// test inner class
public static class Quux {
String str;
Froboz froboz;
public Quux(String str, int fro) {
this.str = str;
froboz = new Froboz(fro);
}
public Froboz getFroboz() { return froboz; }
public void setFroboz(Froboz froboz) { this.froboz = froboz; }
public String getStr() { return str; }
public void setStr(String str) { this.str = str; }
}
// test API
JexlEngine jexl = new JexlBuilder().create();
Quux quux = jexl.newInstance(Quux.class, "xuuq", 100);
jexl.setProperty(quux, "froboz.value", Integer.valueOf(100));
Object o = jexl.getProperty(quux, "froboz.value");
assertEquals("Result is not 100", new Integer(100), o);
jexl.setProperty(quux, "['froboz'].value", Integer.valueOf(1000));
o = jexl.getProperty(quux, "['froboz']['value']");
assertEquals("Result is not 1000", new Integer(1000), o);
表达式和脚本
如果需要简单的表达式评估功能,JEXL的核心特性很可能适合。主要的方法有:
- JexlEngine.createScript(org.apache.commons.jexl3.JexlFeatures, org.apache.commons.jexl3.JexlInfo, java.lang.String, java.lang.String...)
- JexlScript.execute(org.apache.commons.jexl3.JexlContext)
- JexlEngine.createExpression(org.apache.commons.jexl3.JexlInfo, java.lang.String)
- JexlExpression.evaluate(org.apache.commons.jexl3.JexlContext)
下面的示例说明了他们的用法:
JexlEngine jexl = new JexlBuilder().create();
JexlContext jc = new MapContext();
jc.set("quuxClass", quux.class);
JexlExpression create = jexl.createExpression("quux = new(quuxClass, 'xuuq', 100)");
JelxExpression assign = jexl.createExpression("quux.froboz.value = 10");
JexlExpression check = jexl.createExpression("quux[\"froboz\"].value");
Quux quux = (Quux) create.evaluate(jc);
Object o = assign.evaluate(jc);
assertEquals("Result is not 10", new Integer(10), o);
o = check.evaluate(jc);
assertEquals("Result is not 10", new Integer(10), o);
统一表达式和模板
如果您正在寻找类似JSP-EL的基本模板功能,可以使用JxltEngine中的Expression。
主要的方法有:
- JxltEngine.createExpression(java.lang.String)
- JxltEngine.Expression.prepare(org.apache.commons.jexl3.JexlContext)
- JxltEngine.Expression.evaluate(org.apache.commons.jexl3.JexlContext)
- JxltEngine.createTemplate(org.apache.commons.jexl3.JexlInfo, java.lang.String, java.io.Reader, java.lang.String...)
- JxltEngine.Template.prepare(org.apache.commons.jexl3.JexlContext)
- JxltEngine.Template.evaluate(org.apache.commons.jexl3.JexlContext, java.io.Writer)
下面的示例说明了他们的用法:
JexlEngine jexl = new JexlBuilder().create();
JxltEngine jxlt = jexl.createJxltEngine();
JxltEngine.Expression expr = jxlt.createExpression("Hello ${user}");
String hello = expr.evaluate(context).toString();
JexlExpression, JexlScript, Expression and Template
JexlExpression
这些是JexlEngine表达式的最基本形式,只允许执行单个命令并返回结果。如果您尝试使用多个命令,它会忽略第一个分号之后的所有内容,只返回第一个命令的结果。
还要注意,表达式不是语句(这是脚本的组成部分),不允许使用流控制(if,while,for)、变量或lambdas语法元素。
JexlScript
这些允许您使用多个语句,您可以使用变量赋值、循环、计算等。或多或少可以在Shell或JavaScript的基本级别实现这些功能。最后一个命令的结果从脚本返回。
JxltEngine.Expression
这些是生成“一行”文本的理想选择,比如类固醇的“toString()”。要获得计算,可以使用类似于EL的语法,如${someVariable}。括号之间的表达式的行为类似于JexlScript,而不是表达式。您可以使用分号来执行多个命令,最后一个命令的结果将从脚本返回。您还可以使用#{someScript}语法使用两遍评估。
JxltEngine.Template
它们生成文本文档。以“$$”(默认值)开头的每一行都被视为JEXL代码,其他所有行都被认为是JxltEngine.Expression。将这些视为简单的Velocity模板。重写的MudStore初始Velocity示例如下所示:
<html>
<body>
Hello ${customer.name}!
<table>
$$ for(var mud : mudsOnSpecial ) {
$$ if (customer.hasPurchased(mud) ) {
<tr>
<td>
${flogger.getPromo( mud )}
</td>
</tr>
$$ }
$$ }
</table>
</body>
</html>
JEXL 配置
JexlEngine可以通过几个参数进行配置,这些参数将驱动它在发生错误时的反应。这些配置方法通过JexlBuilder嵌入。
静态共享配置
JexlEngine和JxltEngine都是线程安全的,它们的大部分内部字段都是final;同一实例可以在不同线程之间共享,并且在关键区域(内省缓存)中强制执行适当的同步。
特别重要的是JexlBuilder.loader,它指示正在构建的JexlEngine使用哪个类加载器来解决类名;这直接影响JexlEngine.newInstance和“new”脚本方法的操作方式。
在依赖JEXL为应用程序动态加载和调用插件的情况下,这也非常有用。为了避免在插件实现发生更改时重新启动服务器,您可以调用JexlEngine.setClassLoader(java.lang.ClassLoader),通过该引擎实例创建的所有脚本都将自动指向新加载的类。
您可以通过NoJexl注释说明可以通过脚本操作什么,该注释完全屏蔽了类和方法,使其不受JEXL内省的影响。限制JEXL的另一种可配置方式是使用JexlSandbox,它允许更好地控制暴露的内容;沙盒可以通过JexlBuilder.sandbox设置。
命名空间(java.util.Map<java.lang.String,java.lang.Object>)通过将自己的类注册为命名空间来扩展JEXL脚本,允许随意公开自己的函数。
这可用于
public static MyMath {
public double cos(double x) {
return Math.cos(x);
}
}
Map<String, Object> funcs = new HashMap<String, Object>();
funcs.put("math", new MyMath());
JexlEngine jexl = new JexlBuilder().namespaces(funcs).create();
JexlContext jc = new MapContext();
jc.set("pi", Math.PI);
JexlExpression e = JEXL.createExpression("math:cos(pi)");
o = e.evaluate(jc);
assertEquals(Double.valueOf(-1),o);
默认支持的操作:
Operator | Method Name | Example |
---|---|---|
+ | add | add(x, y) |
- | subtract | subtract(x, y) |
* | multiply | multiply(x, y) |
/ | divide | divide(x, y) |
% | mod | mod(x, y) |
& | bitwiseAnd | bitwiseAnd(x, y) |
| | bitwiseOr | bitwiseOr(x, y) |
^ | bitwiseXor | bitwiseXor(x, y) |
! | logicalNot | logicalNot(x) |
- | bitwiseComplement | bitiwiseComplement(x) |
== | equals | equals(x, y) |
< | lessThan | lessThan(x, y) |
<= | lessThanOrEqual | lessThanOrEqual(x, y) |
> | greaterThan | greaterThan(x, y) |
>= | greaterThanOrEqual | greaterThanOrEqual(x, y) |
- | negate | negate(x) |
size | size | size(x) |
empty | empty | empty(x) |