好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

java利用JEXL实现动态表达式编译

背景

做项目突然遇到这样的需求:
系统要获取多个数据源的数据,并进行处理,最后输出多个字段。字段的计算规则一般是简单的取值最多加一点条件判断。
而且需要动态变动!!例如一个字段a的取值,如果a > 10的时候输出10,a <= 10则输出a。这里的10可能在一天后改成8,也可能在后天就改成了12。当然,如果只是一个数字的变动还好说,我们可以使用数据库进行存储。但是,万一哪天需求突然变成了a < 10的时候输出10,a >=10 则输出a,就需要对代码改动,再测试再发布才能到生产环境使用。
一两个这样的字段还没什么,如果整个系统所依赖的字段都有这样的属性,那么我们就需要找一种方法来实现动态的加载逻辑。
下面介绍的JEXL就可以解决这种问题

JEXL(Java Expression Language)介绍

JEXL – Apache Commons JEXL Overview

下面用一些实例来介绍JEXL的使用方法

实例

maven依赖:

?

1

2

3

4

5

< dependency >

     < groupId >org.apache测试数据mons</ groupId >

     < artifactId >commons-jexl</ artifactId >

     < version >2.0</ version >

</ dependency >

正则表达式匹配

首先写一个公共方法:

?

1

2

3

4

5

6

public class Util {

     public static boolean regMatch(String regEx, String str) {

         Pattern pattern = Pattern测试数据pile(regEx);

         return pattern.matcher(str).matches();

     }

}

下面是使用JEXL调用的方法

?

1

2

3

4

5

6

7

8

9

10

11

public void RL() {

     JexlContext jc = new MapContext();

     String str = "一二三四五六七八九十" ;

     jc.set( "Util" , new Util());

     jc.set( "str" , str);

     jc.set( "ans" , "" );

     String expression = "ans = Util.regMatch(\"[\u4e00-\u9fa5]{10,}\",str)" ;

     Expression e = new JexlEngine().createExpression(expression);

     e.evaluate(jc);

     System.out.println(jc.get( "ans" ));

}

代码中的expression变量就是可以动态编译的表达式,这里要注意表达式中出现的所有变量,都需要事先set进JexlContext中,否则会报错。这里有多种形式的错误:

①如果没有set]Util],程序运行中会抛出异常。

org.apache测试数据mons.jexl2.JexlException: TmpTest.RL@40![13,40]: 'ans = QeUtil.regMatch('[一-龥]{10,}', str);' attempting to call method on null

②如果没有set]str],程序不会抛出异常,并输出null。如果你的regMatch方法中有判空处理,就会输出判空的结果。如果没有判空处理,在控制台的输出如下:

警告: TmpTest.RL@39![36,39]: 'ans = QeUtil.regMatch('[一-龥]{10,}', str);' undefined variable str
二月 21, 2017 4:00:41 下午 org.apache测试数据mons.jexl2.JexlEngine invocationFailed
警告: TmpTest.RL@39![13,40]: 'ans = QeUtil.regMatch('[一-龥]{10,}', str);' method invocation error
java.lang.NullPointerException

③如果没有set]ans],程序会正常运行,并输出正确值
为了保险起见,建议表达式中出现的所有变量,都需要事先set进JexlContext中

循环

JEXL支持两种循环方式:

?

1

2

3

for (item : list) {

     x = x + item;

}

?

1

2

3

while (x lt 10 ) {

     x = x + 2 ;

}

下面是使用while的实例:

?

1

2

3

4

5

6

7

8

9

public void loop() {

     JexlContext jc = new MapContext();

     jc.set( "a" , 1 );

     jc.set( "b" , "0" );

     jc.set( "ans" , new StringBuffer());

     Expression e = new JexlEngine().createExpression( "while (a < 10) {a = a + 1;ans.append(b);}" );

     e.evaluate(jc);

     System.out.println(jc.get( "ans" ));

}

get\set方法调用

JEXL支持传入对象,并调用对象的方法
下面的简单的get\set方法的实例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public void getSet() {

     TmpTest tmpTest = new TmpTest();

     tmpTest.setA( 1 );

     JexlContext jc = new MapContext();

     jc.set( "tmpTest" , tmpTest);

     jc.set( "ans" , "" );

     Expression e = new JexlEngine().createExpression( "ans = tmpTest.getA()" );

     e.evaluate(jc);

     System.out.println(jc.get( "ans" ));

     e = new JexlEngine().createExpression( "ans = tmpTest.setA(2)" );

     e.evaluate(jc);

     TmpTest tmpTest1 = (TmpTest) jc.get( "tmpTest" );

     System.out.println(tmpTest1.getA());

}

上面的用例会在控制台先输出1,再输出2

下面是其他网友的评论

ScriptEngine比这个方便n倍,能动态执行js function,了解一下

原文链接:https://blog.csdn.net/u012468264/article/details/56679802

查看更多关于java利用JEXL实现动态表达式编译的详细内容...

  阅读:28次