色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java表達(dá)式引擎選型調(diào)研分析

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-08-15 14:25 ? 次閱讀

1 簡(jiǎn)介

我們項(xiàng)目組主要負(fù)責(zé)面向企業(yè)客戶的業(yè)務(wù)系統(tǒng),企業(yè)的需求往往是多樣化且復(fù)雜的,對(duì)接不同企業(yè)時(shí)會(huì)有不同的定制化的業(yè)務(wù)模型和流程。我們?cè)跇I(yè)務(wù)系統(tǒng)中使用表達(dá)式引擎,集中配置管理業(yè)務(wù)規(guī)則,并實(shí)現(xiàn)實(shí)時(shí)決策和計(jì)算,可以提高系統(tǒng)的靈活性和響應(yīng)能力,從而更好地滿足業(yè)務(wù)的需求。

舉個(gè)簡(jiǎn)單的例子,假設(shè)我們有一個(gè)業(yè)務(wù)場(chǎng)景,在返利系統(tǒng)中,當(dāng)推廣員滿足一定的獎(jiǎng)勵(lì)條件時(shí),就會(huì)給其對(duì)應(yīng)的獎(jiǎng)勵(lì)金額。例如某個(gè)產(chǎn)品的具體獎(jiǎng)勵(lì)規(guī)則如下:

獎(jiǎng)勵(lì)條件 獎(jiǎng)勵(lì)金額
拉新用戶數(shù)大于等于3個(gè)且客單價(jià)大于50元 100元
拉新用戶數(shù)大于等于5個(gè)且客單價(jià)大于100元 200元
拉新用戶數(shù)大于等于10個(gè)且客單價(jià)大于200元 500元

這個(gè)規(guī)則看起來很好實(shí)現(xiàn),只要在代碼里寫幾個(gè)if else分支就可以了。但是如果返利系統(tǒng)對(duì)接了多家供應(yīng)商,且每家提供的產(chǎn)品的獎(jiǎng)勵(lì)規(guī)則都不同呢?再通過硬編碼的方式寫if else似乎就不太好了,每次增加修改刪除規(guī)則都需要系統(tǒng)發(fā)版上線。

引入規(guī)則引擎似乎就能解決這個(gè)問題,規(guī)則引擎的一個(gè)好處就是可以使業(yè)務(wù)規(guī)則和業(yè)務(wù)代碼分離,從而降低維護(hù)難度,同時(shí)它還可以滿足業(yè)務(wù)人員通過編寫DSL或通過界面指定規(guī)則的訴求,這樣就可以在沒有開發(fā)人員參與的情況下建立規(guī)則了,這種說法聽起來似乎很有道理,但在實(shí)踐中卻很少行得通。首先,規(guī)則引擎有一定的學(xué)習(xí)成本,即使開發(fā)人員使用也需要進(jìn)行專門的學(xué)習(xí),更何況沒有任何編程背景的業(yè)務(wù)人員,其次,其實(shí)現(xiàn)的復(fù)雜度也高,如果業(yè)務(wù)規(guī)則復(fù)雜,規(guī)則制定者對(duì)規(guī)則引擎內(nèi)部隱藏的程序流程不了解,很可能會(huì)得到意想不到的結(jié)果,最后,有些規(guī)則引擎還存在性能瓶頸。如果對(duì)規(guī)則引擎和表達(dá)式引擎都不熟悉,抽離的業(yè)務(wù)規(guī)則又需要由開發(fā)人員來制定,那么相比之下表達(dá)式引擎就要容易上手的多,其語法更接近Java,而且有些表達(dá)式引擎還會(huì)將表達(dá)式編譯成字節(jié)碼,在執(zhí)行速度和資源利用方面可能就更有優(yōu)勢(shì)。所以,對(duì)于此類業(yè)務(wù)場(chǎng)景,使用表達(dá)式引擎似乎更加合適一些。

本文主要對(duì)Java表達(dá)式引擎進(jìn)行概要性介紹和分析,并提供一定建議,為團(tuán)隊(duì)研發(fā)過程中對(duì)表達(dá)式引擎的技術(shù)選型提供輸入。

2 技術(shù)棧簡(jiǎn)介

本文將針對(duì)AviatorScript、MVEL、OGNL、SpEL、QLExpress、JEXL、JUEL幾種常見表達(dá)式引擎進(jìn)行選型調(diào)研。先簡(jiǎn)單介紹一下這幾種表達(dá)式引擎。

2.1 AviatorScript

AviatorScript 是一門高性能、輕量級(jí)寄宿于 JVM 之上的腳本語言。AviatorScript 可將表達(dá)式編譯成字節(jié)碼。2010年作者在淘寶中間件負(fù)責(zé)Notify內(nèi)部消息中間件時(shí)開發(fā)并開源。它原來的定位一直只是一個(gè)表達(dá)式引擎,不支持 if/else 條件語句,也不支持for/while循環(huán)語句等,隨著5.0的發(fā)布變身為一個(gè)通用腳本語言,支持了這些語言特性。

文檔:https://www.yuque.com/boyan-avfmj/aviatorscript?

2.2 MVEL (MVFLEX Expression Language)

MVEL是一種混合的動(dòng)態(tài)/靜態(tài)類型的、可嵌入Java平臺(tái)的表達(dá)式語言,MVEL被眾多Java項(xiàng)目使用。MVEL 在很大程度上受到 Java 語法的啟發(fā),但也有一些本質(zhì)區(qū)別,目的是使其作為一種表達(dá)式語言更加高效,例如直接支持集合、數(shù)組和字符串匹配的操作符,以及正則表達(dá)式。最早版本發(fā)布于2007年。

文檔:http://mvel.documentnode.com/?

2.3 OGNL (Object-Graph Navigation Language)

OGNL 是 Object-Graph Navigation Language(對(duì)象圖導(dǎo)航語言)的縮寫;它是一種表達(dá)式語言,用于獲取和設(shè)置 Java 對(duì)象的屬性,以及其他額外功能,如列表投影和選擇以及 lambda 表達(dá)式。于2005年發(fā)布2.1.4版。

文檔:https://commons.apache.org/dormant/commons-ognl/language-guide.html?

2.4 SpEL (Spring Expression Language)

SpEL是一種功能強(qiáng)大的表達(dá)式語言,支持在運(yùn)行時(shí)查詢和操作對(duì)象圖。該語言的語法與 Unified EL 相似,但提供了更多的功能,其中最主要的是方法調(diào)用和基本的字符串模板功能。

文檔:https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/core.html#expressions?

2.5 QLExpress

由阿里的電商業(yè)務(wù)規(guī)則、表達(dá)式(布爾組合)、特殊數(shù)學(xué)公式計(jì)算(高精度)、語法分析、腳本二次定制等強(qiáng)需求而設(shè)計(jì)的一門動(dòng)態(tài)腳本引擎解析工具。在阿里集團(tuán)有很強(qiáng)的影響力,同時(shí)為了自身不斷優(yōu)化、發(fā)揚(yáng)開源貢獻(xiàn)精神,于2012年開源。

文檔:https://github.com/alibaba/QLExpress?

2.6 JEXL (Java Expression Language)

JEXL 旨在促進(jìn)在 Java 編寫的應(yīng)用程序和框架中實(shí)現(xiàn)動(dòng)態(tài)腳本功能。 JEXL 基于對(duì) JSTL 表達(dá)式語言的一些擴(kuò)展實(shí)現(xiàn)了一種表達(dá)式語言,支持 shell 腳本或 ECMAScript 中的大部分構(gòu)想。1.0版發(fā)布于2005年。

文檔:https://commons.apache.org/proper/commons-jexl/reference/syntax.html?

2.7 JUEL (Java Unified Expression Language)

JUEL 是統(tǒng)一表達(dá)式語言 (EL) 的實(shí)現(xiàn),該語言是 JSP 2.1 標(biāo)準(zhǔn) (JSR-245) 的一部分,已在 JEE5 中引入。此外,JUEL 2.2 實(shí)現(xiàn)了 JSP 2.2 維護(hù)版本規(guī)范,完全符合 JEE6 標(biāo)準(zhǔn)。于2006年發(fā)布2.1.0版本,2.2.7發(fā)布于2014年。

文檔:https://juel.sourceforge.net/guide/start.html?

2.8 Janino

Janino是一個(gè)超小、超快的Java編譯器,也可以用作表達(dá)式引擎,它的性能非常出色,根據(jù)官網(wǎng)介紹,Apache Spark、Apache Flink、Groovy等優(yōu)秀的開源項(xiàng)目都在用Janino。

文檔:http://janino-compiler.github.io/janino/?

由于Janino實(shí)際是一個(gè)Java編譯器,理論上其性能應(yīng)該更接近于直接執(zhí)行Java代碼,其次作為表達(dá)式引擎使用起來比較復(fù)雜。因此,下面的對(duì)比中,Janino不參與比較,可以將其作為一個(gè)參照。

2.9 其他

如下一些表達(dá)式引擎雖然也常見于各技術(shù)博客,但由于長(zhǎng)期沒有更新維護(hù),因此沒有納入此次選型比較

Fel

Fel是輕量級(jí)的高效的表達(dá)式計(jì)算引擎。Fel源自于企業(yè)項(xiàng)目,設(shè)計(jì)目標(biāo)是為了滿足不斷變化的功能需求和性能需求。項(xiàng)目托管于Google Code,上次更新是2012年,已經(jīng)十幾年沒有更新了,所以沒有納入此次選型。

ik-expression

IK Expression是一個(gè)開源的(OpenSource),可擴(kuò)展的(Extensible),基于java語言開發(fā)的一個(gè)超輕量級(jí)(Super lightweight)的公式化語言解析執(zhí)行工具包。2009年2月發(fā)布第一個(gè)版本,2009年10月發(fā)布最后一個(gè)版本后再?zèng)]有新版本發(fā)布,所以沒有納入此次選型。

JSEL

JSEL是一個(gè)兼容 JavaScript 運(yùn)算規(guī)則的簡(jiǎn)單的表達(dá)式解釋引擎,你可以通過Map接口,或者JavaBean給出一個(gè)變量集合,能后通過表達(dá)式從這個(gè)集合中抽取變量,再通過表達(dá)式邏輯生成你需要的數(shù)據(jù)。2009年發(fā)布第一個(gè)版本,2011年發(fā)布最后一個(gè)版本后未再更新,所以沒有納入此次選型。

此外規(guī)則引擎如 Drools, urule, easy-rules 等不參與此次選型比較。相對(duì)比較成熟完善的腳本語言如Groovy,JavaScript等也不參與選型比較。這篇文章主要針對(duì)相對(duì)輕量簡(jiǎn)單的表達(dá)式引擎進(jìn)行選型。

?

3 技術(shù)棧選型評(píng)估

選擇表達(dá)式引擎,我們希望其社區(qū)支持情況良好、實(shí)現(xiàn)復(fù)雜度適中、執(zhí)行速度快、安全并且簡(jiǎn)單易學(xué)。所以,接下來將從社區(qū)支持情況、引入的大小和依賴、性能、安全性、使用案例和語法幾個(gè)方面對(duì)幾種表達(dá)式引擎進(jìn)行比較評(píng)估。

3.1 社區(qū)支持情況

社區(qū)支持情況可以輔助評(píng)估項(xiàng)目的健康度,有問題是不是能及時(shí)解決,項(xiàng)目是不是能持續(xù)演進(jìn)等等,下面列出了GitHub star,watch,fork,last commit等數(shù)據(jù),可以作為參考,由于數(shù)據(jù)隨著時(shí)間推移會(huì)產(chǎn)生變化,以下僅針對(duì)2023.10.29的數(shù)據(jù)進(jìn)行分析。

wKgaoma9n0aAR6UUAAHv1GSo6ng800.png

由于 Spring 項(xiàng)目被廣泛使用,而SpEl又是Spring的一個(gè)子項(xiàng)目,所以從各項(xiàng)數(shù)據(jù)來看SpEl的社區(qū)支持情況是最好的。下面先排除SpEl分析其他幾個(gè)表達(dá)式引擎。

QLExpress,AviatorScript 和 MVEL 在國(guó)內(nèi)使用比較多,這可能是他們star,watch,fork數(shù)較高的原因。說明這幾個(gè)項(xiàng)目受歡迎度,受認(rèn)可度,影響力應(yīng)該較高。

從issues,pull requests數(shù)來分析,可以看到 MVEL,AviatorScript 和 QLExpress 高于其他腳本引擎,說明他們的用戶需求和反饋較多,也可能意味著項(xiàng)目面臨較多問題和挑戰(zhàn)。

MVEL,JEXL,OGNL 均有較多貢獻(xiàn)者參與。他們的社區(qū)協(xié)作、項(xiàng)目可持續(xù)性方面應(yīng)該都比較不錯(cuò)。

綜合以上分析,除SpEl外,QLExpress,AviatorScript 和 MVEL 的社區(qū)支持情況都相對(duì)較好。

3.2 引入大小和依賴

代碼大小和依賴可以輔助評(píng)估代碼的復(fù)雜性,下面列出了各個(gè)Github倉庫的代碼大小,可以作為一個(gè)參考(實(shí)際并不完全準(zhǔn)確反映其實(shí)現(xiàn)的復(fù)雜性)。

以下是2023.10.29的數(shù)據(jù)

wKgZoma9n0eAUI8GAAB3GU2YzdI772.png

JUEL,QLExpress代碼大小最小,都在600多KB;其次是 OGNL 1MB多一點(diǎn);AviatorScript,MVEL,JEXL 大小都在2MB左右;SpEl由于在 spring-framework 倉庫中,上表中統(tǒng)計(jì)的是 spring-framework 的總量,單純看 SpEl 的模塊 spring-expression 的話,大小是1.3MB左右。但是其還依賴了 spring-core 和 spring-jcl,再含這兩個(gè)的話,大小 7.4MB左右。

我們?cè)俳Y(jié)合各個(gè)項(xiàng)目的依賴來分析一下。

+- org.mvel:mvel2:jar:2.5.0.Final:compile
+- com.googlecode.aviator:aviator:jar:5.3.3:compile
+- com.alibaba:QLExpress:jar:3.3.1:compile
|  +- commons-beanutils:commons-beanutils:jar:1.8.2:compile
|  |  - (commons-logging:commons-logging:jar:1.1.1:compile - omitted for conflict with 1.2)
|  - commons-lang:commons-lang:jar:2.4:compile
+- org.codehaus.janino:janino:jar:3.1.10:compile
|  - org.codehaus.janino:commons-compiler:jar:3.1.10:compile
+- ognl:ognl:jar:3.4.2:compile
|  - org.javassist:javassist:jar:3.29.2-GA:compile
+- org.apache.commons:commons-jexl3:jar:3.3:compile
|  - commons-logging:commons-logging:jar:1.2:compile
+- org.springframework:spring-expression:jar:5.3.29:compile
|  - org.springframework:spring-core:jar:5.3.29:compile
|     - org.springframework:spring-jcl:jar:5.3.29:compile
+- de.odysseus.juel:juel-api:jar:2.2.7:compile
+- de.odysseus.juel:juel-impl:jar:2.2.7:compile
+- de.odysseus.juel:juel-spi:jar:2.2.7:compile

除了SpEl外,QLExpress,OGNL,JEXL也都有其他依賴。

如果考慮 commons-beanutils, commons-lang, commons-logging 三個(gè)依賴,QLExpress 引入的大小在 10MB左右。

如果考慮 javassist 依賴,OGNL 引入的大小是4MB多。

如果考慮 commons-logging 依賴,JEXL 引入的大小是2.5MB左右。

綜合來看,JUEL,AviatorScript,MVEL,JEXL 在引入大小和依賴方面要好于其他。

3.3 性能

較好的性能意味著系統(tǒng)能夠快速地響應(yīng)用戶的請(qǐng)求,減少等待時(shí)間,提升體驗(yàn)。

性能方面主要通過 JMH 在字面量表達(dá)式、含有變量的表達(dá)式以及含有方法調(diào)用的表達(dá)式等使用場(chǎng)景對(duì)幾個(gè)表達(dá)式引擎進(jìn)行測(cè)試。

JMH(Java Microbenchmark Harness),是用于代碼微基準(zhǔn)測(cè)試的工具套件,主要是基于方法層面的基準(zhǔn)測(cè)試,精度可以達(dá)到納秒級(jí)。該工具是由 Oracle 內(nèi)部實(shí)現(xiàn) JIT 的大牛們編寫的,他們應(yīng)該比任何人都了解 JIT 以及 JVM 對(duì)于基準(zhǔn)測(cè)試的影響。

由于不同表達(dá)式引擎語法或特性稍有差別,下面測(cè)試中對(duì)于差異項(xiàng)會(huì)進(jìn)行說明。

性能測(cè)試代碼地址: GitHub

3.3.1 字面量表達(dá)式

wKgaoma9n0iAZjXAAABlVz2JeOs974.svg

:1000 + 100.0 * 99 - (600 - 3 * 15) / (((68 - 9) - 3) * 2 - 100) + 10000 % 7 * 71

:6.7 - 100 > 39.6 ? 5 == 5 ? 4 + 5 : 6 - 1 : !(100 % 3 - 39.0 < 27) ? 8 * 2 - 199 : 100 % 3

說明:

由于QlExpress執(zhí)行第2個(gè)表達(dá)式時(shí)報(bào)錯(cuò),需要增加圓括號(hào),實(shí)際執(zhí)行的是6.7 - 100 > 39.6 ? (5 == 5 ? 4 + 5 : 6 - 1) : (!(100 % 3 - 39.0 < 27) ? 8 * 2 - 199 : 100 % 3)

結(jié)果分析:

可以明顯看到 JEXL,JUEL,QlExpress這三個(gè)表達(dá)式引擎性能明顯不如其他引擎。

SpEl 在執(zhí)行第1個(gè)算數(shù)操作時(shí)表現(xiàn)出色,但是在執(zhí)行第2個(gè)嵌套三元操作時(shí)明顯不如AviatorScript,MVEL,OGNL引擎。

此輪測(cè)試中 AviatorScript,OGNL,MVEL表現(xiàn)出色。AviatorScript,OGNL 執(zhí)行兩個(gè)表達(dá)式表現(xiàn)都比較出色,其中AviatorScript略好于OGNL。 MVEL 在執(zhí)行第1個(gè)算數(shù)操作時(shí)表現(xiàn)最出色,但是在執(zhí)行第2個(gè)嵌套三元操作時(shí)慢于AviatorScript,OGNL引擎。

3.3.2 含有變量的表達(dá)式

wKgZoma9n0iAcUPXAACaPsqvY70443.svg

:pi * d + b - (1000 - d * b / pi) / (pi + 99 - i * d) - i * pi * d / b

:piDecimal * dDecimal + bDecimal - (1000 - dDecimal * bDecimal / piDecimal) / (piDecimal + 99 - iDecimal * dDecimal) - iDecimal * piDecimal * dDecimal / bDecimal

:i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99 == i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99

:(clientVersion == '1.9.0' || clientVersion == '1.9.1' || clientVersion == '1.9.2') && deviceType == 'Xiaomi' && weight >= 4 && osVersion == 'Android 9.0' && osType == 'Android' && clientIp != null && requestTime <= now&& customer.grade > 1 && customer.age > 18

說明:

?由于不同的表達(dá)式引擎在執(zhí)行第2個(gè)表達(dá)式時(shí)底層實(shí)現(xiàn)除法時(shí)有所差別,MVEL,AviatorScript,JEXL 執(zhí)行decimal.divide(otherDecimal, java.math.MathContext.DECIMAL128),其他實(shí)際執(zhí)行的是decimal.divide(otherDecimal, scale, roundingMode),只是參數(shù)略有不同,分析時(shí)分組進(jìn)行。

?由于QlExpress執(zhí)行第3個(gè)表達(dá)式時(shí)報(bào)錯(cuò),不支持非整型mod操作,需要增加類型轉(zhuǎn)換,實(shí)際執(zhí)行的是i * pi + (d * b - 199) / (1 - d * pi) - (int)(2 + 100 - i / pi) % 99 == i * pi + (d * b - 199) / (1 - d * pi) - (int)(2 + 100 - i / pi) % 99

?由于AviatorScript執(zhí)行第4個(gè)表達(dá)式時(shí)報(bào)錯(cuò),null的字面量是nil,實(shí)際執(zhí)行的是(clientVersion == '1.9.0' || clientVersion == '1.9.1' || clientVersion == '1.9.2') && deviceType == 'Xiaomi' && weight >= 4 && osVersion == 'Android 9.0' && osType == 'Android' && clientIp != nil && requestTime <= now&& customer.grade > 1 && customer.age > 18

結(jié)果分析:

第1個(gè)基本類型包裝類的算術(shù)計(jì)算 SpEl 最優(yōu)。其次是AviatorScript,MVEL,OGNL。而JEXL,JUEL,QlExpress則不如其他引擎。

第2個(gè)BigDecimal類型的算術(shù)計(jì)算。由于底層實(shí)現(xiàn)不同,分為兩組。第1組 MVEL、AviatorScript和JEXL,AviatorScript 優(yōu)于 MVEL 優(yōu)于 JEXL。第2組 JUEL,QlExpress,OGNL和SpEl,性能由優(yōu)到差依次是 OGNL,SpEl,JUEL,QlExpress。并且第1組由于精度更高,性能明顯都差于第2組。

第3個(gè)含有基本類型包裝類算數(shù)計(jì)算的布爾表達(dá)式。SpEl 最優(yōu),AviatorScript 次之,接下來依次是 OGNL, MVEL,JUEL,JEXL,QlExpress。

第4個(gè)含有字符串比較的布爾表達(dá)式。AviatorScript,MVEL,JEXL,OGNL 性能優(yōu)于 JUEL,QlExpress,SpEl。

3.3.3 含有方法調(diào)用的表達(dá)式

wKgaoma9n0mAYg8QAACFUVcigX0526.svg

:new java.util.Date()

:s.substring(b.d)

:s.substring(b.d).substring(a, b.c.e)

說明:

?由于 JUEL 執(zhí)行new java.util.Date()時(shí)報(bào)錯(cuò),不支持new實(shí)例,本輪實(shí)際執(zhí)行的是自定義函數(shù)fn:date()

?由于 AviatorScript 執(zhí)行s.substring時(shí)報(bào)錯(cuò),需使用其提供的內(nèi)部函數(shù),本輪實(shí)際執(zhí)行的是其內(nèi)部函數(shù)string.substring

結(jié)果分析:

此輪測(cè)試中 SpEl 的表現(xiàn)最優(yōu),甚至比Janino還要快。MVEL,AviatorScript次之,在執(zhí)行構(gòu)造方法時(shí)MVEL要好于AviatorScript。JEXL 表現(xiàn)也比較出色。QlExpress,JUEL,OGNL這三個(gè)表達(dá)式引擎則不如其他引擎。

3.3.4 總結(jié)

綜合以上測(cè)試結(jié)果,AviatorScript,SpEl,MVEL,OGNL性能表現(xiàn)相對(duì)較好。

AviatorScript 性能相對(duì)較好,表現(xiàn)均衡,但其語法相較其他引擎跟Java的差異略大。

SpEl 除了在個(gè)別場(chǎng)景下性能較差,大部分場(chǎng)景表現(xiàn)非常出色,尤其是在字面量和含有變量的算數(shù)計(jì)算及方法調(diào)用場(chǎng)景下。

MVEL 性能表現(xiàn)相對(duì)均衡,含有變量的算術(shù)計(jì)算略差于AviatorScript,其在字面量算術(shù)計(jì)算,方法調(diào)用場(chǎng)景下表現(xiàn)都非常出色。

OGNL 性能表現(xiàn)也相對(duì)均衡,但方法調(diào)用場(chǎng)景下表現(xiàn)不佳。

3.4 安全

引入表達(dá)式引擎,應(yīng)該重視系統(tǒng)的安全性和可靠性,比如要防止在不可信環(huán)境中被注入惡意腳本,越權(quán)執(zhí)行某些系統(tǒng)命令或使應(yīng)用停止服務(wù)等。安全性方面主要通過漏洞披露、安全指南和配置比較幾種表達(dá)式引擎。

3.4.1 漏洞

首先在https://cve.mitre.org/cve/search_cve_list.html通過關(guān)鍵字搜索的方式粗略了解一下不同表達(dá)式引擎被公開的漏洞。這種方式可能不是非常的準(zhǔn)確,由于不同表達(dá)式引擎的使用場(chǎng)景、使用方式、關(guān)注度的不同可能導(dǎo)致被公開的漏洞存在差異。比如我們所熟悉的 OGNL、SpEl 的關(guān)鍵字出現(xiàn)在漏洞中的頻率明顯高于其他表達(dá)式引擎。OGNL 在MyBatis和Struts中被使用,SpEl則在Spring中被廣泛使用,這兩個(gè)表達(dá)式引擎會(huì)被大部分項(xiàng)目間接使用,直接將用戶輸入作為表達(dá)式的一部分執(zhí)行,很容易導(dǎo)致出現(xiàn)漏洞。

我們可以從這些公布的漏洞中了解不同表達(dá)式引擎可能存在的安全隱患及其修復(fù)情況,在使用過程中盡可能避免出現(xiàn)類似問題。

此外,不推薦將表達(dá)式執(zhí)行直接開放到不可信的環(huán)境,如果確實(shí)需要,應(yīng)該詳細(xì)了解選擇的表達(dá)式引擎,是否提供了必要的設(shè)置選項(xiàng)可以避免某些安全隱患。

名稱 關(guān)鍵字鏈接 漏洞數(shù)
AviatorScript https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=AviatorScript 1
MVEL https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=MVEL 4
OGNL https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=OGNL 28
SpEl https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=SpEl 10
QLExpress https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=QLExpress 0
JEXL https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=JEXL 3
JUEL https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=JUEL 1

3.4.2 安全設(shè)置

AviatorScript,QLExpress,JEXL均從不同程度提供了一些安全選項(xiàng)設(shè)置。

AviatorScript

?設(shè)置白名單

// 在new語句和靜態(tài)方法調(diào)用中允許使用的類白名單 默認(rèn) null 表示無限制
AviatorEvaluator.setOption(Options.ALLOWED_CLASS_SET, Sets.newHashSet(List.class));
// 在new語句和靜態(tài)方法調(diào)用中允許使用的類白名單 包含子類 默認(rèn) null 表示無限制
AviatorEvaluator.setOption(Options.ASSIGNABLE_ALLOWED_CLASS_SET, Sets.newHashSet(List.class));

?防止死循環(huán)

// 循環(huán)最大次數(shù) 默認(rèn) 0 表示無限制
AviatorEvaluator.setOption(Options.MAX_LOOP_COUNT, 10000);

?特性開關(guān)

// 關(guān)閉某些特性
AviatorEvaluator.getInstance().disableFeature(Feature.Module);
AviatorEvaluator.getInstance().disableFeature(Feature.NewInstance);
// 只開啟需要的特性
AviatorEvaluator.setOption(Options.FEATURE_SET, Feature.asSet(Feature.If));

QLExpress

?開啟沙箱模式

QLExpressRunStrategy.setSandBoxMode(true);

在沙箱模式中,不可以:

?import Java 類

?顯式引用 Java 類,比如String a = 'mmm'

?取 Java 類中的字段:a = new Integer(11); a.value

?調(diào)用 Java 類中的方法:Math.abs(12)

可以:

?使用 QLExpress 的自定義操作符/宏/函數(shù),以此實(shí)現(xiàn)與應(yīng)用的受控交互

?使用. 操作符獲取 Map 的 key 對(duì)應(yīng)的 value,比如 a 在應(yīng)用傳入的表達(dá)式中是一個(gè) Map,那么可以通過 a.b 獲取

?所有不涉及應(yīng)用 Java 類的操作

?設(shè)置白名單

// 設(shè)置編譯期白名單
QLExpressRunStrategy.setCompileWhiteCheckerList(Arrays.asList(
    // 精確設(shè)置
    CheckerFactory.must(Date.class),
    // 子類設(shè)置
    CheckerFactory.assignable(List.class)
));
// 設(shè)置運(yùn)行時(shí)白名單// 必須將該選項(xiàng)設(shè)置為 true
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
// 有白名單設(shè)置時(shí), 則黑名單失效
QLExpressRunStrategy.addSecureMethod(RiskBean.class, "secureMethod");

?設(shè)置黑名單

// 必須將該選項(xiàng)設(shè)置為 true
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
// 這里不區(qū)分靜態(tài)方法與成員方法, 寫法一致
// 不支持重載, riskMethod 的所有重載方法都會(huì)被禁止
QLExpressRunStrategy.addSecurityRiskMethod(RiskBean.class, "riskMethod");

QLExpess 目前默認(rèn)添加的黑名單有:

?java.lang.System.exit

?java.lang.Runtime.exec

?java.lang.ProcessBuilder.start

?java.lang.reflect.Method.invoke

?java.lang.reflect.Class.forName

?java.lang.reflect.ClassLoader.loadClass

?java.lang.reflect.ClassLoader.findClass

?防止死循環(huán)

//可通過timeoutMillis參數(shù)設(shè)置腳本的運(yùn)行超時(shí)時(shí)間:1000ms
Object r = runner.execute(express, context, null, true, false, 1000);

?

JEXL

?使用沙箱

// 使用中應(yīng)該通過JexlSandbox的重載構(gòu)造方法進(jìn)行配置
new JexlBuilder().sandbox(new JexlSandbox()).create();

?設(shè)置白名單權(quán)限

new JexlBuilder().permissions(JexlPermissions.RESTRICTED.compose("com.jd.*")).create();

?特性開關(guān)

// 關(guān)閉循環(huán)、new 實(shí)例,import等特性
new JexlBuilder().features(new JexlFeatures().loops(false).newInstance(false).importPragma(false)).create();

?

3.5 使用案例

從業(yè)界使用情況可以了解不同表達(dá)式引擎的可行性、生態(tài)和整合性,以及最佳實(shí)踐,進(jìn)而借鑒。從下表可以看到AviatorScript,MVEL,QLExpress在國(guó)內(nèi)業(yè)務(wù)線均有使用案例,有些企業(yè)也有文章輸出,我們可以借鑒使用。

名稱 案例
AviatorScript 美團(tuán)酒旅實(shí)時(shí)數(shù)據(jù)規(guī)則引擎應(yīng)用實(shí)踐,liteflow,京東星鏈
MVEL 閑魚億級(jí)商品庫中的秒級(jí)實(shí)時(shí)選品,easy-rules,compileflow,京東星鏈
OGNL MyBatis,Struts
SpEl Spring
QLExpress compileflow,liteflow,阿里內(nèi)部業(yè)務(wù)線
JEXL cat,Jelly
JUEL JSP
Janino Apache Spark、Apache Flink、Groovy

3.6 語法

易于理解和使用的語法可以提高開發(fā)效率,并降低學(xué)習(xí)成本。接下來從類型、操作符、控制語句、集合、方法定義幾方面比較一下不同表達(dá)式引擎的語法設(shè)計(jì)。

類型方面,AviatorScript 設(shè)計(jì)了特有的類型,使用時(shí)需要注意其類型轉(zhuǎn)換的優(yōu)先級(jí)long->bigint->decimal->double。AviatorScript、MVEL、OGNL、JEXL都支持BigInteger、BigDecimal字面量,這意味著進(jìn)行精確計(jì)算時(shí)可以使用字面量,將更方便,如10.24B就表示一個(gè)BigDecimal字面量(AviatorScript中BigDecimal字面量后綴是M)。此外AviatorScript、QLExpress還支持高精度計(jì)算的設(shè)置項(xiàng)。

操作符方面,QLExpress支持替換、自定義操作符及添加操作符別名,這可能有助于簡(jiǎn)化復(fù)雜表達(dá)式或使表達(dá)式更加直觀,不過添加預(yù)置函數(shù)應(yīng)該可以達(dá)到差不多的效果。AviatorScript也支持自定義部分操作符,不過支持?jǐn)?shù)量相當(dāng)有限。AviatorScript、SpEl、JEXL支持正則匹配操作符。

控制語句方面,除OGNL、SpEl、JUEL不支持控制語句外,其他都支持,不過需要注意 AviatorScript 的 else if 語法有些特殊寫作 elsif,foreach語句跟Java也有所不同。

集合方面,除JUEL外其他都提供了快捷定義的方式,只不過語法不同。

函數(shù)定義方面,SpEl、JUEL均不支持,OGNL支持偽lambda定義,其他都支持定義函數(shù)。QLExpress不支持定義lambda。

綜合來看,和Java語法都或多或少存在一些差異。AviatorScript設(shè)計(jì)了自己特有的一些語法,使用的話需要熟悉一下。QLExpress支持自定義操作符,可以使表達(dá)式看起來更直觀。MVEL、JEXL的語法可能更接近Java,讓人更容易接受一些。OGNL、SpEl、JUEL的語法更簡(jiǎn)單一些,不支持控制語句和函數(shù)定義,當(dāng)然也可以通過預(yù)置一些函數(shù)變通解決一些較復(fù)雜的問題。

?

4 選型建議

社區(qū)方面,SpEl無疑是最活躍的。AviatorScript,QLExpress,MVEL在國(guó)內(nèi)很受歡迎,QLExpress 有阿里背書。

代碼大小和依賴方面,AviatorScript,MVEL 依賴少,并且代碼大小也偏小。

性能方面,如果你使用表達(dá)式引擎執(zhí)行字面量算術(shù)計(jì)算或方法調(diào)用偏多可以選用SpEl,MVEL。如果希望整體性能表現(xiàn)較好可以選用 AviatorScript。

安全方面,如果想自定義安全選項(xiàng),可以考慮 AviatorScript,QLExpress和JEXL。

使用案例方面,AviatorScript,MVEL,QLExpress在國(guó)內(nèi)都有實(shí)際使用案例可循。

語法方面,可能存在一些主觀因素,僅供參考,個(gè)人覺得MVEL、JEXL的語法設(shè)計(jì)使用起來會(huì)更容易一些。

通過對(duì)以上幾個(gè)方面的評(píng)估和分析,希望可以幫助團(tuán)隊(duì)基于自身情況及偏好選擇最適合自己項(xiàng)目的Java表達(dá)式引擎。

?

5 參考資料

QLExpress: https://github.com/alibaba/QLExpress?

AviatorScript: https://github.com/killme2008/aviatorscript?

MVEL: https://github.com/mvel/mvel?

OGNL: https://github.com/orphan-oss/ognl?

SpEl: https://github.com/spring-projects/spring-framework?

Janino: https://github.com/janino-compiler/janino?

JUEL: https://github.com/beckchr/juel?

JEXL: https://github.com/apache/commons-jexl?

Fel: https://github.com/dbcxy/fast-el?

ik-expression: https://code.google.com/archive/p/ik-expression/?

JSEL: https://code.google.com/archive/p/lite/wikis/JSEL.wiki?

JMH: https://www.cnblogs.com/wupeixuan/p/13091381.html

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2973

    瀏覽量

    104885
  • 引擎
    +關(guān)注

    關(guān)注

    1

    文章

    361

    瀏覽量

    22591
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    輕量級(jí)Java表達(dá)式引擎aviator的基本使用

    aviator本來是一個(gè)輕量級(jí)、高性能的基于JVM的表達(dá)式引擎。不過從5.0.0版本開始,aviator升級(jí)成為了aviatorScript,成為一個(gè)高性能、輕量級(jí)寄宿于 JVM (包括 Android 平臺(tái))之上的腳本語言。
    發(fā)表于 09-13 09:42 ?799次閱讀

    Java Lambda表達(dá)式的新特性

    Java Lambda表達(dá)式Java 8中最重要的新特性之一。 它們是一種可傳遞的匿名函數(shù),可以作為參數(shù)傳遞給方法或存儲(chǔ)在變量中,因此可以在需要的時(shí)候調(diào)用它們。 基礎(chǔ) 1. 簡(jiǎn)介 Lambda
    的頭像 發(fā)表于 09-30 10:29 ?2613次閱讀

    什么是正則表達(dá)式?正則表達(dá)式如何工作?哪些語法規(guī)則適用正則表達(dá)式

    實(shí)現(xiàn)自動(dòng)化文本處理。在許多編程語言中,正則表達(dá)式都被廣泛用于文本處理、數(shù)據(jù)分析、網(wǎng)頁抓取等領(lǐng)域。通過正則表達(dá)式,我們可以精確地篩選、操作和格式化文本,提高工作效率。
    的頭像 發(fā)表于 11-03 14:41 ?3777次閱讀
    什么是正則<b class='flag-5'>表達(dá)式</b>?正則<b class='flag-5'>表達(dá)式</b>如何工作?哪些語法規(guī)則適用正則<b class='flag-5'>表達(dá)式</b>?

    shell正則表達(dá)式學(xué)習(xí)

    程序設(shè)計(jì)語言都支持利用正則表達(dá)式進(jìn)行字符串操作。例如,在Perl中就內(nèi)建了一個(gè)功能強(qiáng)大的正則表達(dá)式引擎。正則表達(dá)式在檢索或替換上遠(yuǎn)比之前用到的通配符要強(qiáng)大的多,用在批處理上絕對(duì)好用,比
    發(fā)表于 07-25 17:18

    防范表達(dá)式的失控

    在C 語言中,表達(dá)式是最重要的組成部分之一,幾乎所有的代碼都由表達(dá)式構(gòu)成。表達(dá)式的使用如此廣泛,讀者也許會(huì)產(chǎn)生這樣的疑問,像+ 、- 、3 、/ 、& & 這樣簡(jiǎn)單的運(yùn)算也會(huì)出現(xiàn)
    發(fā)表于 04-22 16:57 ?13次下載

    關(guān)于java正則表達(dá)式的用法詳解

    正則表達(dá)式 一個(gè)正則表達(dá)式是一個(gè)用于文本搜索的文本模式。換句話說,在文本中搜索出現(xiàn)的模式。例如,你可以用正則表達(dá)式搜索網(wǎng)頁中的郵箱地址或超鏈接。 正則表達(dá)式示例 下面是一個(gè)簡(jiǎn)單的
    發(fā)表于 09-27 14:24 ?0次下載

    Python正則表達(dá)式的學(xué)習(xí)指南

    本文介紹了Python對(duì)于正則表達(dá)式的支持,包括正則表達(dá)式基礎(chǔ)以及Python正則表達(dá)式標(biāo)準(zhǔn)庫的完整介紹及使用示例。本文的內(nèi)容不包括如何編寫高效的正則表達(dá)式、如何優(yōu)化正則
    發(fā)表于 09-15 08:00 ?0次下載
    Python正則<b class='flag-5'>表達(dá)式</b>的學(xué)習(xí)指南

    Python正則表達(dá)式指南

    本文介紹了Python對(duì)于正則表達(dá)式的支持,包括正則表達(dá)式基礎(chǔ)以及Python正則表達(dá)式標(biāo)準(zhǔn)庫的完整介紹及使用示例。本文的內(nèi)容不包括如何編寫高效的正則表達(dá)式、如何優(yōu)化正則
    發(fā)表于 03-26 09:13 ?10次下載
    Python正則<b class='flag-5'>表達(dá)式</b>指南

    Lambda表達(dá)式詳解

    C++11中的Lambda表達(dá)式用于 **定義并創(chuàng)建匿名的函數(shù)對(duì)象** ,以簡(jiǎn)化編程工作。下面看一下Lambda表達(dá)式的基本構(gòu)成。
    的頭像 發(fā)表于 02-09 11:28 ?1198次閱讀

    表達(dá)式與邏輯門之間的關(guān)系

    邏輯表達(dá)式是指表示一個(gè)表示邏輯運(yùn)算關(guān)系的式子,是一個(gè)抽象的類似數(shù)學(xué)表達(dá)式,下面我們重點(diǎn)說明下其表達(dá)式與邏輯門之間的關(guān)系。
    的頭像 發(fā)表于 02-15 14:54 ?1656次閱讀
    <b class='flag-5'>表達(dá)式</b>與邏輯門之間的關(guān)系

    C語言的表達(dá)式

    在C語言中,表達(dá)式是由操作符和操作數(shù)組成。表達(dá)式可以由一個(gè)或者多個(gè)操作數(shù)組成,不同的操作符與操作數(shù)組成不同的表達(dá)式,因此,表達(dá)式才是C語言的基本。
    的頭像 發(fā)表于 02-21 15:09 ?1394次閱讀
    C語言的<b class='flag-5'>表達(dá)式</b>

    一文詳解Verilog表達(dá)式

    表達(dá)式由操作符和操作數(shù)構(gòu)成,其目的是根據(jù)操作符的意義得到一個(gè)計(jì)算結(jié)果。表達(dá)式可以在出現(xiàn)數(shù)值的任何地方使用。
    的頭像 發(fā)表于 05-29 16:23 ?2877次閱讀
    一文詳解Verilog<b class='flag-5'>表達(dá)式</b>

    如何使用lambda表達(dá)式提升開發(fā)效率?

    Java8 的一個(gè)大亮點(diǎn)是引入 Lambda 表達(dá)式,使用它設(shè)計(jì)的代碼會(huì)更加簡(jiǎn)潔。當(dāng)開發(fā)者在編寫 Lambda 表達(dá)式時(shí),也會(huì)隨之被編譯成一個(gè)函數(shù)式接口。
    發(fā)表于 08-24 10:25 ?310次閱讀

    zabbix觸發(fā)器表達(dá)式 基本RS觸發(fā)器表達(dá)式 rs觸發(fā)器的邏輯表達(dá)式

    zabbix觸發(fā)器表達(dá)式 基本RS觸發(fā)器表達(dá)式 rs觸發(fā)器的邏輯表達(dá)式? Zabbix是一款開源的監(jiān)控軟件,它能通過監(jiān)控指標(biāo)來實(shí)時(shí)監(jiān)測(cè)服務(wù)器和網(wǎng)絡(luò)的運(yùn)行狀態(tài),同時(shí)還能提供警報(bào)和報(bào)告等功能來幫助管理員
    的頭像 發(fā)表于 08-24 15:50 ?1618次閱讀

    一文詳解Java表達(dá)式引擎選型

    AviatorScript 是一門高性能、輕量級(jí)寄宿于 JVM 之上的腳本語言。AviatorScript 可將表達(dá)式編譯成字節(jié)碼。它原來的定位一直只是一個(gè)表達(dá)式引擎,不支持 if/else 條件
    的頭像 發(fā)表于 12-06 10:57 ?1950次閱讀
    一文詳解<b class='flag-5'>Java</b><b class='flag-5'>表達(dá)式</b><b class='flag-5'>引擎</b><b class='flag-5'>選型</b>
    主站蜘蛛池模板: 蜜桃传媒在线播放| 亚洲高清在线视频| 欧美videqsdesex0| 欧美最猛12teevideos欧美| 青青草原在线免费| 善良的女房东味道2在线观看| 四虎永久在线精品国产| 亚洲AV久久无码精品九号| 伊人久久影院| a圾片目录大全| 国产CHINESE HD精品| 国产乱码二卡3卡四卡| 簧片在线观看| 麻豆高清免费国产一区| 日本19禁啪啪吃奶大尺度| 外国xxxx| 一个人色导航| TUBE8最新日本护士| 国产精品嫩草影院| 久久99r66热这里有精品| 男人有噶坏| 王雨纯羞羞| 在线播放一区二区精品产| sm主人调教揉花蒂H| 国产午夜精品理论片免费观看| 久久国产免费| 日韩欧美中文字幕在线二视频| 亚洲AV综合99一二三四区| 2022精品福利在线小视频| 公开超碰在线视频| 精品 在线 视频 亚洲| 欧美激情一区二区三区视频| 无码AV毛片色欲欧洲美洲| 与邻居换娶妻子2在线观看| xxww69| 国产午夜不卡| 名女躁b久久天天躁| 午夜性爽视频男人的天堂在线| 欲香欲色天天天综合和网| 成视频高清| 久久久久久久久久综合情日本|