Jexl表达式引擎(2)

JEXL是一个旨在促进应用程序和框架中动态和脚本功能实现的库。

Example:

在最简单的形式中,JEXL 在评估表达式时将JexlExpression 与 JexlContext 合并。使用JexlEngine.createExpression(java.lang.String)传递包含有效JEXL语法的String 创建表达式 。可以使用MapContext实例创建简单的JexlContext ; 可以通过其构造函数选择提供内部包装的变量映射。以下示例采用名为car的变量,并在属性engine上调用checkStatus()方法

        // Create a JexlEngine (could reuse one instead)
        JexlEngine jexl = new JexlBuilder().create();
        // Create an expression object equivalent to 'car.getEngine().checkStatus()':
        String jexlExp = "car.engine.checkStatus()";
        Expression e = jexl.createExpression( jexlExp );
        // The car we have to handle coming as an argument...
        Car car = theCarThatWeHandle;
        // Create a context and add data
        JexlContext jc = new MapContext();
        jc.set("car", car );
        // Now evaluate the expression, getting the result
        Object o = e.evaluate(jc);

使用JEXL
API由三个级别组成,以满足不同的功能需求:

  • 动态调用setter,getters,方法和构造函数
  • 脚本表达式称为JEXL表达式
  • JSP / JSF之类的表达式称为JXLT表达式

重要的提示:
公共API类位于2个包中:
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.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,表达式和模板:摘要

JexlExpression
这些是JexlEngine表达式的最基本形式,只允许执行单个命令并返回其结果。如果您尝试使用多个命令,它会忽略第一个分号后的所有内容,只返回第一个命令的结果。

还要注意表达式不是语句(这是脚本的组成),并且不允许使用流控制(if,while,for),变量或lambdas语法元素。

JexlScript
这些允许您使用多个语句,您可以使用变量赋值,循环,计算等。或多或少可以在Shell或JavaScript的基本级别实现。从脚本返回最后一个命令的结果。

JxltEngine.Expression
这些是生成“单行”文本的理想选择,就像toString()一样。要获得计算,请使用类似EL的语法,如$ {someVariable}。括号之间的表达式行为类似于JexlScript,而不是表达式。您可以使用分号来执行多个命令,并从脚本返回最后一个命令的结果。您还可以使用#{someScript}语法使用2-pass评估。

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都是线程安全的,他们的大部分内部领域都是最终的; 可以在不同线程之间共享相同的实例,并在关键区域(内省缓存)中实施适当的同步。

特别重要的是JexlBuilder.loader,它指示JexlEngine正在构建哪个类加载器用于解决类名; 这直接影响JexlEngine.newInstance和'new'脚本方法的运行方式。

在您依赖JEXL动态加载和调用应用程序插件的情况下,这也非常有用。为了避免在插件实现更改时重新启动服务器,您可以调用 JexlEngine.setClassLoader(java.lang.ClassLoader),通过此引擎实例创建的所有脚本将自动指向新加载的类。

您可以通过NoJexl 完全屏蔽JEXL内省的类和方法的注释来说明可以通过脚本操作的内容。限制JEXL的另一种可配置方式是使用一个 JexlSandbox允许更好地控制暴露内容的方法; 沙箱可以通过JexlBuilder.sandbox。

JexlBuilder.namespaces 通过将您自己的类注册为名称空间来扩展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);

如果命名空间是一个Class,并且该类声明了一个带有JexlContext(或扩展JexlContext的类)的构造函数,则在表达式中首次使用时会创建一个命名空间实例; 此实例生命周期仅限于表达式评估。

也可以配置JexlEngine和JxltEngine表达式缓存。如果您打算在应用程序中重复使用JEXL,那么这些值得配置,因为表达式解析非常繁重。请注意,JEXL创建的所有缓存都是通过SoftReference保存的; 在高内存压力下,GC将能够回收这些缓存,如果需要,JEXL将重建它们。默认情况下,JexlEngine会为“小”表达式创建缓存,而JxltEngine会为Expression创建一个缓存。

JexlBuilder.cache将设置JEXL引擎可以同时缓存多少个表达式。JxltEngine允许通过其构造函数定义缓存大小。

JexlBuilder.debug 使得JExlException携带的堆栈跟踪更有意义; 特别是,这些跟踪将携带表达式创建的确切调用者位置。

动态配置
这些配置选项可以在评估期间通过实现JexlContext 实现JexlEngine.Options带有评估选项的实现来覆盖 。测试包中存在这样的类的示例。

JexlBuilder.strict或者JexlEngine.Options.isStrict() 在JEXL在各种情况下将'null'视为错误时进行配置; 当面对一个不可引用的变量时,使用null作为算术运算符的参数或者无法调用方法或构造函数。宽松模式接近JEXL-1.1行为。

JexlBuilder.silent或JexlEngine.Options.isSilent() 配置JEXL如何对错误做出反应; 如果是静默的,引擎将不会抛出异常但会通过记录器发出警告并在出现错误时返回null。请注意,当非静默时,JEXL会抛出JexlException,这是未经检查的异常。

JexlContext.NamespaceResolver通过JexlContext 实现- 以 JexlEvalContext 为例 - 允许覆盖命名空间解析和通过定义的默认命名空间映射JexlBuilder.namespaces。

JEXL定制
这是JexlContext,JexlBuilder并且 JexlEngine.Options是您希望实现自定义的最可能的接口。由于它们暴露变量和选项,因此它们是主要目标。在此之前,请查看测试目录中的JexlEvalContext,ObjectContext这可能已经涵盖了您的一些需求。

JexlArithmetic 如果您需要更改运算符的行为方式或添加运算类型,则派生类。有3个入口点可以自定义创建的对象类型:

  • array literals: JexlArithmetic.arrayBuilder(int)
  • map literals: JexlArithmetic.mapBuilder(int)
  • set literals: JexlArithmetic.setBuilder(int)
  • range objects: JexlArithmetic.createRange(java.lang.Object, java.lang.Object)

您还可以重载运算符方法; 按照惯例,每个运算符都有一个与之关联的方法名称。如果在JexlArithmetic派生实现中重载某些方法,则当参数与方法签名匹配时,将调用这些方法。例如,如果你想要'+'来操作数组,就会出现这种情况; 你需要派生JexlArithmetic并实现'public Object add(Set x,Set y)'方法。但请注意,您无法更改运算符优先级。运算符/方法匹配列表如下:

OperatorMethod NameExample
+addadd(x, y)
-subtractsubtract(x, y)
*multiplymultiply(x, y)
/dividedivide(x, y)
%modmod(x, y)
&bitwiseAndbitwiseAnd(x, y)
IbitwiseOrbitwiseOr(x, y)
^bitwiseXorbitwiseXor(x, y)
!logicalNotlogicalNot(x)
-bitwiseComplementbitiwiseComplement(x)
==equalsequals(x, y)
<lessThanlessThan(x, y)
<=lessThanOrEquallessThanOrEqual(x, y)
>greaterThangreaterThan(x, y)
>=greaterThanOrEqualgreaterThanOrEqual(x, y)
-negatenegate(x)
sizesizesize(x)
emptyemptyempty(x)

您还可以添加方法来重载属性getter和setter操作符行为。名为propertyGet / propertySet / arrayGet / arraySet的JexlArithmetic实例的公共方法是在适当时调用的潜在替代。下表概述了语法形式与调用方法之间的关系,其中V是属性值类,O是对象类,P是属性标识符类(通常是String或Integer)。

ExpressionMethod Template
foo.propertypublic V propertyGet(O obj, P property);
foo.property = valuepublic V propertySet(O obj, P property, V value);
foo[property]public V arrayGet(O obj, P property, V value);
foo[property] = valuepublic V arraySet(O obj, P property, V value);

您还可以覆盖基本运算符方法,其参数为Object的方法可以为您提供完全控制。

扩展JEXL
如果您需要使JEXL以特殊方式处理某些对象或调整它对某些设置的反应,您可以获得其大部分内部工作。这些类和方法很少是私有的或最终的 - 只有当内部合同真正需要它时。但是,使用受保护的方法和内部包类意味着在发布新的JEXL版本时,可能必须重新调整代码。
Engine可以扩展为允许您捕获自己的配置默认值wrt缓存大小和各种标志。实现自己的缓存 - 而不是基于LinkedHashMap的基本缓存 - 将是另一种可能的扩展。

Interpreter 如果您需要向评估本身添加更多功能,那么该类是派生的类; 例如,您希望变量的前置和后置解析器或变量上下文的嵌套作用域。

Uberspect 如果需要为某些对象添加内省或反射功能,则可以派生类,例如将基于工厂的支持添加到“new”运算符。代码已经将公共字段反映为Java-beans约定之上的属性。

原文链接:http://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/package-summary.html#package.description

转载于:https://www.cnblogs.com/Jmmm/p/10610535.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值