1. 程式人生 > >Jexl表達式語言引擎(2)

Jexl表達式語言引擎(2)

parser variable etc 賦值 範圍 rip setvalue expr 自己的

包org.apache.commons.jexl3描述
提供用於評估JEXL表達式的框架。

介紹
簡要例子
使用JEXL
配置JEXL
自定義JEXL
擴展JEXL
介紹
JEXL是一個旨在促進應用程序和框架中動態和腳本功能實現的庫。

一個簡短的例子
在最簡單的形式中,JEXL 在評估表達式時將a JexlExpression 與a 合並 JexlContext。使用JexlEngine.createExpression(java.lang.String)傳遞包含有效JEXL語法的String 創建表達式 。可以使用MapContext實例創建簡單的JexlContext ; 可以通過其構造函數選擇提供內部包裝的變量映射。以下示例采用名為“car”的變量,並在屬性“engine”上調用checkStatus()方法

        //創建一個JexlEngine(可以重用一個)
        JexlEngine jexl = new JexlBuilder()。create();
        //創建一個等同於'car.getEngine()。checkStatus()'的表達式對象:
        String jexlExp =“car.engine.checkStatus()”;
        表達式e = jexl.createExpression(jexlExp);
        //我們必須處理的汽車作為一個論點......
        汽車車=車裏的WeHandle;
        //創建上下文並添加數據
        JexlContext jc = new MapContext();
        jc.set(“car”,car);
        //現在評估表達式,得到結果
        對象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...)
以下示例說明了它們的用法:
//測試外層
public static class Froboz {
int值;
public Froboz(int v){value = v; }
public void setValue(int v){value = v; }
public int getValue(){return value; }
}
//測試內部類
public static class Quux {
String str;
Froboz froboz;
public Quux(String str,int fro){
this.str = str;
froboz = new Froboz(來自);
}
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; }
}
//測試API
JexlEngine jexl = new JexlBuilder()。create();
Quux quux = jexl.newInstance(Quux.class,“xuuq”,100);
jexl.setProperty(quux,“froboz.value”,Integer.valueOf(100));
對象o = jexl.getProperty(quux,“froboz.value”);
assertEquals(“結果不是100”,新的整數(100),o);
jexl.setProperty(quux,“[‘froboz‘]。value”,Integer.valueOf(1000));
o = jexl.getProperty(quux,“[‘froboz‘] [‘value‘]”);
assertEquals(“結果不是1000”,新的整數(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);
        對象o = assign.evaluate(jc);
        assertEquals(“結果不是10”,新的整數(10),o);
        o = check.evaluate(jc);
        assertEquals(“結果不是10”,新的整數(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遍評估。

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個入口點可以自定義創建的對象類型:

數組文字: JexlArithmetic.arrayBuilder(int)
地圖文字: JexlArithmetic.mapBuilder(int)
設置文字: JexlArithmetic.setBuilder(int)
範圍對象: JexlArithmetic.createRange(java.lang.Object, java.lang.Object)
您還可以重載運算符方法; 按照慣例,每個運算符都有一個與之關聯的方法名稱。如果在JexlArithmetic派生實現中重載某些方法,則當參數與方法簽名匹配時,將調用這些方法。例如,如果你想要‘+‘來操作數組,就會出現這種情況; 你需要派生JexlArithmetic並實現‘public Object add(Set x,Set y)‘方法。但請註意,您無法更改運算符優先級。運算符/方法匹配列表如下:

操作者 方法名稱 例

  • 加 添加(x,y)
  • 減去 減去(x,y)
  • 乘 乘以(x,y)
    / 劃分 除(x,y)
    % MOD mod(x,y)
    & BITWISEAND bitwiseAnd(x,y)
    | bitwiseOr bitwiseOr(x,y)
    ^ bitwiseXor bitwiseXor(x,y)
    ! logicalNot logicalNot(x)的
  • bitwiseComplement bitiwiseComplement(x)的
    == 等於 等於(x,y)
    < 少於 lessThan(x,y)
    <= lessThanOrEqual lessThanOrEqual(x,y)

    比...更棒 greaterThan(x,y)
    = greaterThanOrEqual greaterThanOrEqual(x,y)

  • 否定 否定(x)的
    尺寸 尺寸 大小(x)的
    空 空 空(x)的
    您還可以添加方法來重載屬性getter和setter操作符行為。名為propertyGet / propertySet / arrayGet / arraySet的JexlArithmetic實例的公共方法是在適當時調用的潛在替代。下表概述了語法形式與調用方法之間的關系,其中V是屬性值類,O是對象類,P是屬性標識符類(通常是String或Integer)。

表達 方法模板
foo.property 公共V propertyGet(O obj,P property);
foo.property = value public V propertySet(O obj,P property,V value);
FOO [屬性] public V arrayGet(O obj,P property,V value);
foo [property] = value public V arraySet(O obj,P property,V value);
您還可以覆蓋基本運算符方法,其參數為Object的方法可以為您提供完全控制。

擴展JEXL
如果您需要使JEXL以特殊方式處理某些對象或調整它對某些設置的反應,您可以獲得其大部分內部工作。這些類和方法很少是私有的或最終的 - 只有當內部合同真正需要它時。但是,使用受保護的方法和內部包類意味著在發布新的JEXL版本時,可能必須重新調整代碼。
Engine可以擴展為允許您捕獲自己的配置默認值wrt緩存大小和各種標誌。實現自己的緩存 - 而不是基於LinkedHashMap的基本緩存 - 將是另一種可能的擴展。

Interpreter 如果您需要向評估本身添加更多功能,那麽該類是派生的類; 例如,您希望變量的前置和後置解析器或變量上下文的嵌套作用域。

Uberspect 如果需要為某些對象添加內省或反射功能,則可以派生類,例如將基於工廠的支持添加到“new”運算符。代碼已經將公共字段反映為Java-beans約定之上的屬性。

Jexl表達式語言引擎(2)