JEXL 是一个表达式语言引擎,可以在应用程序或框架中实现动态和脚本功能。先写一个简单的例子,最直观的感受下。
依赖
首先我们是引入依赖包,添加pom文件中的dependency
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl3</artifactId>
<version>3.1</version>
</dependency>
基础用法
再新建一个测试用的对象类
public class TestJexlObj {
private int numA;
private int numB;
//get.set方法省略
}
之后我们来写一份测试逻辑用的代码
@Test
public void test() {
TestJexlObj testObj = new TestJexlObj();
testObj.setNumA(12);
testObj.setNumB(9);
JexlEngine jexl = new Engine();
JexlExpression expression = jexl.createExpression("'numA is '+testObj.getNumA()+' numB is '+testObj.getNumB()");
JexlContext jc = new MapContext();
jc.set("testObj", testObj);
String str = String.valueOf(expression.evaluate(jc));
System.out.println(str);
JexlExpression expression2 = jexl.createExpression("'numA + numB = '+(testObj.getNumA()+testObj.getNumB())");
String str2 = String.valueOf(expression2.evaluate(jc));
System.out.println(str2);
}
执行后我们可以看到如下结果
以及一份多行构建java对象的表达式作为Script执行的示例代码
JexlContext jc = new MapContext();
jc.set("TestJexlObjClass",TestJexlObj.class);
JexlScript expression3 = jexl.createScript(
"testObj2 = new (TestJexlObjClass);" +
"testObj2.setNumA(1919);"+
"testObj2.setNumB(54);" +
"return testObj2;");
TestJexlObj obj2 = (TestJexlObj)expression3.execute(jc);
System.out.println(obj2.getNumA()+", "+obj2.getNumB());
可以得到如下结果
表达式默认支持一些操作,如下
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) |
自定义扩展函数
如果上述的这些操作不满足你的需求,或者你想要更加简洁地封装起你的表达式逻辑,则可以通过下面的方法,将自己的类注册为命名空间来扩展JEXL脚本,来实现更加复杂的逻辑
新建一个包含你逻辑方法的类
public static class IncClass{
public void inc(TestIncObj testIncObj){
testIncObj.num++;
}
}
以及你可能需要用到的对象
public class TestIncObj {
public int num = 0;
}
之后来实现主要逻辑
public void funcTest(){
Map<String, Object> funcs = new HashMap<>();
funcs.put("IncClass", new IncClass());
JexlEngine jexlInc = new JexlBuilder().namespaces(funcs).create();
TestIncObj testIncObj = new TestIncObj();
testIncObj.num = 0;
JexlContext jc = new MapContext();
jc.set("testIncObj", testIncObj);
JexlExpression e = jexlInc.createExpression("IncClass:inc(testIncObj)");
e.evaluate(jc);
System.out.println(testIncObj.num);
}
这段代码我们可以看到,将IncClass类加入进Jexl的命名空间,这样我们就可以调用IncClass自带的inc方法。通过inc方法的调用来实现对TestIncObj对象的num成员变量进行++操作。执行代码可以看到结果输出为1,表示确实num变量被执行了++操作。
关于线程安全
简单写个demo看下
public void test2() {
TestJexlObj testObj = new TestJexlObj();
testObj.setNumA(0);
CountDownLatch countDownLatch = new CountDownLatch(2);
final int[] num = {0};
AtomicInteger integer = new AtomicInteger(0);
JexlEngine jexl = new Engine();
JexlContext jc = new MapContext();
jc.set("testObj", testObj);
JexlExpression expression = jexl.createExpression("testObj.incNumA()");
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
num[0]++;
expression.evaluate(jc);
integer.getAndIncrement();
}
countDownLatch.countDown();
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
num[0]++;
expression.evaluate(jc);
integer.getAndIncrement();
}
countDownLatch.countDown();
});
thread1.start();
thread2.start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(testObj.getNumA());
System.out.println(num[0]);
System.out.println(integer.get());
}
public class TestJexlObj {
private int numA;
public int getNumA() {
return numA;
}
public int incNumA(){
return numA++;
}
public void setNumA(int numA) {
this.numA = numA;
}
}
可以很直观的看到执行出来的结果JexlExpression在执行evaluate的时候不会去保证原子性,和正常的方法调用的时候是一样的,需要开发人员自行保证原子性。
官方文档中有这么一段描述
Both JexlEngine and JxltEngine are thread-safe, most of their inner fields are final; the same instance can be shared between different threads and proper synchronization is enforced in critical areas (introspection caches).
大意是指JexlEngine和JxltEngine都是线程安全的,它们的大部分内部字段都是final;同一实例可以在不同线程之间共享,并且在关键区域(内省缓存)中强制执行适当的同步。这里说的是指
JexlEngine jexl = new Engine();
对象可在不同线程之间进行共享,具体各自执行的表达式之间不会有线程安全问题
那么到这里,关于JexlEngine的使用用我们已经有了一个基本的了解。下一步,我们将把JexlEngine结合之前的文章《基于AOP的Redis缓存注解功能开发设计》的内容,重新设计调整下,来解决该文章中提到的hashKey生成规则的问题
相关官方文档:
发表评论