Java 式エンジン選択の調査と分析 | JD Cloud 技術チーム

1 はじめに

私たちのプロジェクトチームは主に企業顧客向けのビジネスシステムを担当しています。企業のニーズは多様かつ複雑であることが多く、異なる企業と連携する場合、カスタマイズされたビジネスモデルやプロセスも異なります。ビジネス システムで式エンジンを使用して、ビジネス ルールを一元的に構成および管理し、リアルタイムの意思決定と計算を実装します。これにより、システムの柔軟性と応答性が向上し、ビジネス ニーズをより適切に満たすことができます。

簡単な例として、リベート システムでは、プロモーターが一定の報酬条件を満たした場合、それに応じた報酬額がプロモーターに与えられます。たとえば、特定の製品の具体的な報酬ルールは次のとおりです。

報酬条件 報酬額
新規ユーザー数が3人以上、単価が50元以上 100元
新規ユーザー数が5人以上、単価が100元以上 200元
新規ユーザー数が10人以上、単価が200元以上 500元

このルールは実装が簡単のようです。コード内に if else 分岐をいくつか記述するだけです。しかし、リベート システムが複数のサプライヤーに接続されており、各企業が異なる報酬ルールで異なる製品を提供している場合はどうなるでしょうか? if else をハードコード的に記述するのは良くないようで、ルールを追加、変更、削除するたびにシステムをオンラインで解放する必要があります。

この問題はルールエンジンの導入で解決できそうです。ルールエンジンのメリットとしては、業務ルールと業務コードを分離できるので保守の難易度が下がり、同時にニーズにも応えられることです。開発者の関与なしでルールを確立できるという考えは合理的に聞こえるかもしれませんが、実際にはそれが機能することはほとんどありません。第一に、ルール エンジンには一定の学習コストがかかり、プログラミングの知識のないビジネス担当者はもちろん、開発者であっても使用する際には特別な学習が必要であること、第二に、その実装の複雑さも高いことです。ルール エンジン内の隠れたプログラム フローを理解していないと、予期しない結果が生じる可能性があり、最後に、一部のルール エンジンには依然としてパフォーマンスのボトルネックが存在します。ルール エンジンと式エンジンに詳しくなく、抽象化されたビジネス ルールを開発者が作成する必要がある場合は、式エンジンの方がはるかに使いやすく、その構文は Java に近く、式エンジンの一部の機能も使用できます。式をバイトコードにコンパイルすると、実行速度とリソース使用率の点でより多くの利点が得られる可能性があります。**したがって、この種のビジネス シナリオでは、式エンジンを使用する方が適切であると思われます。

この記事では主に、Java 式エンジンの簡単な紹介と分析を提供し、チームの研究開発プロセス中に式エンジンの技術的選択のためのインプットを提供するための特定の提案を提供します。

2 テクノロジースタックの紹介

この記事では、AviatorScriptMVELOGNLSpELQLExpressJEXLJUELなどのいくつかの一般的な式エンジンについて選択調査を実施します。まず、これらの表現エンジンについて簡単に紹介します。

2.1 AviatorScript

AviatorScript は、JVM 上でホストされる高性能で軽量のスクリプト言語です。AviatorScript は式をバイトコードにコンパイルします。2010 年に、著者は淘宝網ミドルウェアの責任者だったときに、Notify 内部メッセージ ミドルウェアを開発し、オープンソース化しました。その元の位置付けは常に単なる式エンジンであり、if/else 条件ステートメントや for/while ループ ステートメントなどはサポートしていません。5.0 のリリースでは、一般的なスクリプト言語に変換され、これらをサポートしています。言語の特徴。

ドキュメント: https://www.yuque.com/boyan-avfmj/aviatorscript

2.2 MVEL (MVFLEX 式言語)

MVEL は、Java プラットフォームに組み込むことができる動的/静的混合型の式言語であり、多くの Java プロジェクトで使用されています。MVEL は Java 構文から大きく影響を受けていますが、コレクション、配列、文​​字列一致の演算子や正規表現の直接サポートなど、式言語としての効率を高めることを目的とした基本的な違いがいくつかあります。最初のバージョンは 2007 年にリリースされました。

ドキュメント: http://mvel.documentnode.com/

2.3 OGNL (オブジェクト グラフ ナビゲーション言語)

OGNL は Object-Graph Navigation Language の略で、Java オブジェクトのプロパティを取得および設定するために使用される式言語であり、リストの射影や選択、ラムダ式などのその他の追加機能も含まれます。バージョン 2.1.4 は 2005 年にリリースされました。

ドキュメント: https://commons.apache.org/dormant/commons-ognl/ language-guide.html

2.4 SpEL (Spring 式言語)

SpEL は、実行時のオブジェクト グラフのクエリと操作をサポートする強力な式言語です。この言語の構文は Unified EL に似ていますが、メソッド呼び出しや基本的な文字列テンプレート機能など、より多くの機能を提供します。

ドキュメント: https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/core.html#expressions

2.5 QLExpress

電子商取引ビジネス ルール、式 (ブール値の組み合わせ)、特殊な数式計算 (高精度)、構文分析、および二次スクリプトのカスタマイズに対するアリババの強いニーズに基づいて設計された動的スクリプト エンジン解析ツールで、2012 年にオープンソース化されました。

ドキュメント: https://github.com/alibaba/QLExpress

2.6 JEXL (Java 式言語)

JEXL は、Java で作成されたアプリケーションおよびフレームワークでの動的スクリプト機能を容易にするように設計されています。JEXL は、JSTL 式言語の拡張機能に基づいた式言語を実装し、シェル スクリプトまたは ECMAScript のほとんどのアイデアをサポートします。バージョン 1.0 は 2005 年にリリースされました。

ドキュメント: https://commons.apache.org/proper/commons-jexl/reference/syntax.html

2.7 JUEL (Java 統合表現言語)

JUEL は、JSP 2.1 標準 (JSR-245) の一部であり、JEE5 で導入された統一式言語 (EL) の実装です。さらに、JUEL 2.2 は JSP 2.2 メンテナンス バージョン仕様を実装し、JEE6 標準に完全に準拠しています。バージョン 2.1.0 は 2006 年にリリースされ、2.2.7 は 2014 年にリリースされました。

ドキュメント: https://juel.sourceforge.net/guide/start.html

2.8 ヨアニナ

Janino は、式エンジンとしても使用できる超小型・超高速 Java コンパイラーであり、そのパフォーマンスは非常に優れており、公式 Web サイトによると、Apache Spark、Apache Flink、Groovy などの優れたオープンソース プロジェクトはすべて使用しています。ジャニノ。

ドキュメント: http://janino-compiler.github.io/janino/

Janino は実際には Java コンパイラであるため、そのパフォーマンスは理論的には Java コードを直接実行するのに近いはずですが、第 2 に、式エンジンとして使用するのはより複雑です。したがって、以下の比較ではジャニーノは比較に参加せず、参考として使用できます。

2.9 その他

次の式エンジンの一部もさまざまなテクノロジー ブログでよく見られますが、長期間更新および保守されていないため、この選択の比較には含まれていません。

Fel は軽量で効率的な式計算エンジンです。Fel はエンタープライズ プロジェクトから生まれ、変化する機能要件とパフォーマンス要件を満たすように設計されました。このプロジェクトは Google Code でホストされています。最後の更新は 2012 年で、10 年以上更新されていないため、この選択には含まれていませんでした。

I式

IK Expression は、Java 言語に基づいて開発された、オープンソース (OpenSource)、拡張可能 (Extensible)、超軽量 (Super Lightweight) の数式言語解析実行ツールキットです。最初のバージョンは 2009 年 2 月にリリースされ、最後のバージョンが 2009 年 10 月にリリースされて以降、新しいバージョンがリリースされていないため、この選択には含まれませんでした。

あなたはそうでした

JSEL は、JavaScript 操作ルールと互換性のあるシンプルな式解釈エンジンです。Map インターフェイスまたは JavaBean を通じて変数セットを指定し、式を通じてこのセットから変数を抽出し、式ロジックを使用して必要なデータを生成できます。最初のバージョンは 2009 年にリリースされ、最後のバージョンは 2011 年にリリースされ、更新されていないため、この選択には含まれていません。

さらに、Drools、urule、easy-rules などのルール エンジンは、この選択比較には参加しません。Groovy などの比較的成熟した完全なスクリプト言語は、選択の比較には含まれていません。この記事では、比較的軽量でシンプルな式エンジンの選択に焦点を当てます。

3 テクノロジースタックの選択評価

式エンジンを選択するときは、優れたコミュニティ サポート、適度な実装の複雑さ、高速な実行、セキュリティ、そして学習が容易であることが望まれます。したがって、次のステップでは、コミュニティ サポート、導入されたサイズと依存関係、パフォーマンス、セキュリティ、ユース ケース、構文の側面からいくつかの式エンジンを比較および評価します。

3.1 コミュニティサポート

コミュニティ サポートは、問題が時間内に解決できるかどうか、プロジェクトが進化し続けることができるかどうかなど、プロジェクトの健全性を評価するのに役立ちます。以下は、GitHub のスター、ウォッチ、フォーク、最後のコミット、およびその他のデータのリストです。データは時間の経過とともに変化するため、以下の分析は 2023.10.29 のデータにのみ基づいています。

Spring プロジェクトは広く使用されており、SpEl は Spring のサブプロジェクトであるため、SpEl のコミュニティ サポートはさまざまなデータ ポイントから最も優れています。まず、他のいくつかの式エンジンを SpEl 分析から除外しましょう。

QLExpress、AviatorScript、MVEL は中国で広く使用されており、それがスター数、ウォッチ数、フォーク数が多い理由である可能性があります。これは、これらのプロジェクトの人気、認知度、影響力が比較的高いはずであることを示しています。

問題とプル リクエストの数を分析すると、MVEL、AviatorScript、QLExpress が他のスクリプト エンジンよりも高いことがわかります。これは、これらのエンジンにはより多くのユーザー ニーズとフィードバックがあることを示しており、これはプロジェクトがより多くの問題や課題に直面していることも意味している可能性があります。

MVEL、JEXL、OGNL にはいずれも多くのコントリビューターが参加しています。コミュニティの協力とプロジェクトの持続可能性は比較的良好であるはずです。

上記の分析に基づくと、SpEl を除いて、QLExpress、AviatorScript、および MVEL は比較的良好なコミュニティ サポートを備えています。

3.2 サイズと依存関係の紹介

コード サイズと依存関係は、コードの複雑さを評価するのに役立ちます。各 Github リポジトリのコード サイズは以下にリストされており、参考として使用できます (実際のコード サイズは実装の複雑さを完全に正確に反映しているわけではありません)。

以下は2023.10.29のデータです。

JUEL と QLExpress のコード サイズは最も小さく、どちらも 600 KB 以上で、次に OGNL のサイズは 1MB 強、AviatorScript、MVEL、および JEXL のサイズはすべて約 2MB、S​​pEl は Spring Framework ウェアハウスにあり、上の表の統計は spring-framework からのもので、SpEl のモジュール spring-expression だけを見ると、合計サイズは約 1.3MB です。ただし、spring-core と spring-jcl にも依存しており、これら 2 つを含めると、サイズは約 7.4MB になります。

各プロジェクトの依存関係に基づいて分析してみましょう。

+- 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 の 3 つの依存関係を考慮すると、QLExpress によって導入されるサイズは約 10MB になります。

javassist の依存関係を考慮すると、OGNL によって導入されたサイズは 4MB を超えます。

commons-logging の依存関係を考慮すると、JEXL によって導入されたサイズは約 2.5MB になります。

総合すると、JUEL、AviatorScript、MVEL、JEXL は、導入されたサイズと依存関係の点で他のものよりも優れています。

3.3 パフォーマンス

パフォーマンスの向上とは、システムがユーザーのリクエストに迅速に応答し、待ち時間を短縮し、エクスペリエンスを向上させることを意味します。

パフォーマンスの観点から、JMH は主に、リテラル式、変数を含む式、メソッド呼び出しを含む式などの使用シナリオで複数の式エンジンをテストするために使用されます。

JMH (Java Microbenchmark Harness) は、コード マイクロ ベンチマーク テスト用のツール スイートで、主にメソッド レベルのベンチマーク テストに基づいており、精度はナノ秒レベルに達します。このツールは、ベンチマーク テストに対する JIT と JVM の影響を誰よりもよく理解している Oracle 社内の JIT 専門家によって作成されました。

異なる式エンジンの構文や機能は若干異なるため、その違いについては以下のテストで説明します。

パフォーマンス テスト コードのアドレス: GitHub

3.3.1 リテラル表現

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 は 2 番目の式を実行するとエラーを報告するため、括弧を追加する必要があります。実際に実行されるのは次のとおりです。6.7 - 100 > 39.6 ? (5 == 5 ? 4 + 5 : 6 - 1) : (!(100 % 3 - 39.0 < 27) ? 8 * 2 - 199 : 100 % 3)

結果分析:

JEXL、JUEL、QlExpress の 3 つの表現エンジンのパフォーマンスが他のエンジンほど良くないことは明らかです。

SpEl は、最初の算術演算を実行する場合には良好なパフォーマンスを発揮しますが、2 番目のネストされた三項演算を実行する場合には、AviatorScript、MVEL、および OGNL エンジンよりも大幅に劣ります。

AviatorScript、OGNL、および MVEL は、このラウンドのテストで良好なパフォーマンスを示しました。AviatorScript と OGNL は両方の式の実行において比較的良好なパフォーマンスを示しますが、AviatorScript は OGNL よりわずかに優れています。MVEL は最初の算術演算を実行するときに最高のパフォーマンスを発揮しますが、2 番目のネストされた三項演算を実行するときは AviatorScript および OGNL エンジンよりも遅くなります。

3.3.2 変数を含む式

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

例証します:

  • 式エンジンが異なると、2 番目の式を実行する際の除算の根本的な実装が異なるため、MVEL、AviatorScript、JEXL がそれを実行します。その他は実際に実行されますが、パラメータが若干decimal.divide(otherDecimal, java.math.MathContext.DECIMAL128)異なりdecimal.divide(otherDecimal, scale, roundingMode)、分析中にグループ化されます。
  • QlExpress は 3 番目の式の実行時にエラーを報告し、非整数 mod 演算をサポートしていないため、型変換を追加する必要があります。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 は 4 番目の式の実行時にエラーを報告するため、null のリテラル値は nil であり、実際の実行は(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

結果分析:

第1の基本型ラッパークラスの算術計算SpElが最適です。続いて、AviatorScript、MVEL、OGNL が続きます。また、JEXL、JUEL、QlExpress は他のエンジンほど優れていません。

2 番目の BigDecimal 型の算術計算。基礎となる実装が異なるため、それらは 2 つのグループに分けられます。グループ 1 MVEL、AviatorScript、JEXL。AviatorScript は MVEL よりも優れており、JEXL よりも優れています。グループ 2 JUEL、QlExpress、OGNL、および SpEl。最高から最低のパフォーマンスは、OGNL、SpEl、JUEL、QlExpress です。さらに、グループ 1 のパフォーマンスは、精度が高いため、グループ 2 のパフォーマンスよりも明らかに劣っています。

3 番目には、基本タイプのラッパー クラスの算術計算を含むブール式が含まれています。SpEl が最も優れており、次に AviatorScript、OGNL、MVEL、JUEL、JEXL、QlExpress と続きます。

4 番目には、文字列比較のためのブール式が含まれています。AviatorScript、MVEL、JEXL、OGNL は、JUEL、QlExpress、SpEl よりもパフォーマンスが優れています。

3.3.3 メソッド呼び出しを含む式

new java.util.Date()

s.substring(b.d)

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

例証します:

  • JUEL はnew java.util.Date()実行時にエラーを報告し、新しいインスタンスをサポートしていないため、このラウンドの実際の実行はカスタム関数です。fn:date()
  • AviatorScript は実行時にエラーを報告するためs.substring、AviatorScript が提供する内部関数を使用する必要がありますが、今回実際に実行されるのは内部関数です。string.substring
  • 結果分析:

SpEl はこのラウンドのテストで最高のパフォーマンスを示し、Janino よりもさらに速かった。コンストラクターを実行する場合、MVEL とそれに続く AviatorScript は、AviatorScript よりも優れています。JEXLも比較的好調です。QlExpress、JUEL、OGNL の 3 つの式エンジンは、他のエンジンほど優れていません。

3.3.4 概要

上記のテスト結果に基づくと、AviatorScript、SpEl、MVEL、および OGNL は比較的良好なパフォーマンスを示します。

AviatorScript は比較的優れたパフォーマンスとバランスの取れたパフォーマンスを備えていますが、その構文は他のエンジンや Java とは若干異なります。

SpEl は、一部のシナリオでパフォーマンスが低下することを除いて、ほとんどのシナリオ、特にリテラル、変数を含む算術計算、およびメソッド呼び出しシナリオで非常に優れたパフォーマンスを発揮します。

MVEL のパフォーマンスは比較的バランスが取れています。変数を含む算術計算は AviatorScript よりもわずかに劣りますが、リテラル算術計算やメソッド呼び出しシナリオでは非常に優れたパフォーマンスを発揮します。

OGNL のパフォーマンスも比較的バランスが取れていますが、メソッド呼び出しシナリオではパフォーマンスが低下します。

3.4 セキュリティ

式エンジンを導入する場合は、信頼できない環境に悪意のあるスクリプトが挿入されたり、特定のシステム コマンドが許可なく実行されたり、アプリケーションが停止したりすることを防ぐなど、システムのセキュリティと信頼性に注意を払う必要があります。セキュリティの観点では、主に脆弱性の開示、セキュリティ ガイド、構成を通じて、いくつかの式エンジンが比較されます。

3.4.1 脆弱性

まず、キーワード検索を使用して、 https://cve.mitre.org/cve/search_cve_list.htmlでさまざまな表現エンジンで公開されている脆弱性を大まかに理解します。この方法はあまり正確ではない可能性があり、使用シナリオ、使用方法、表現エンジンの注意レベルが異なるため、開示される脆弱性には差異が生じる可能性があります。たとえば、私たちがよく知っている OGNL や SpEl のキーワードは、他の表現エンジンよりもはるかに頻繁に脆弱性として出現します。OGNL はMyBatisStrutsで使用され、SpEl は Spring で広く使用されています。これら 2 つの式エンジンはほとんどのプロジェクトで間接的に使用され、ユーザー入力を式の一部として直接実行するため、脆弱性が発生しやすくなります

これらの公開された脆弱性から、さまざまな式エンジンでの潜在的なセキュリティ リスクとその修復について学び、使用中に同様の問題を回避するように努めることができます

また、信頼できない環境に対して式の実行を直接開くことはお勧めできません。本当に必要な場合は、選択した式エンジンについて、また特定のセキュリティ リスクを回避するために必要な設定オプションが提供されているかどうかを確認する必要があります。

名前 キーワードリンク 脆弱性の数
アビエータースクリプト 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
ゲーム https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=SpEl 10
QLExpress https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=QLExpress 0
ジェクスル https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=JEXL 3
宝石 https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=JUEL 1

3.4.2 セキュリティ設定

AviatorScript、QLExpress、JEXL はすべて、さまざまな程度のセキュリティ オプション設定を提供します。

アビエータースクリプト

  • ホワイトリストを設定する
// 在new语句和静态方法调用中允许使用的类白名单 默认 null 表示无限制
AviatorEvaluator.setOption(Options.ALLOWED_CLASS_SET, Sets.newHashSet(List.class));
// 在new语句和静态方法调用中允许使用的类白名单 包含子类 默认 null 表示无限制
AviatorEvaluator.setOption(Options.ASSIGNABLE_ALLOWED_CLASS_SET, Sets.newHashSet(List.class));

  • 無限ループを防ぐ
// 循环最大次数 默认 0 表示无限制
AviatorEvaluator.setOption(Options.MAX_LOOP_COUNT, 10000);

  • 機能スイッチ
// 关闭某些特性
AviatorEvaluator.getInstance().disableFeature(Feature.Module);
AviatorEvaluator.getInstance().disableFeature(Feature.NewInstance);
// 只开启需要的特性
AviatorEvaluator.setOption(Options.FEATURE_SET, Feature.asSet(Feature.If));

QLExpress

  • サンドボックスモードを有効にする
QLExpressRunStrategy.setSandBoxMode(true);

サンドボックス モードでは、次のことはできません。

Java クラスをインポートする
Java クラスへの明示的な参照。String a = 'mmm'
◦ Java クラスのフィールドを取得します。 a = new Integer(11); a.value
Java クラスのメソッドを呼び出します。Math.abs(12)

できる:

QLExpress のカスタム演算子/マロス/関数を使用して、アプリケーションとの制御された対話を実現します。
マップのキーに対応する値を取得するには、. 演算子を使用します 。たとえば、アプリケーションによって渡された式内の a がマップである場合、ab を通じて取得できます。
Java クラスの適用を含まないすべての操作
  • ホワイトリストを設定する
// 设置编译期白名单
QLExpressRunStrategy.setCompileWhiteCheckerList(Arrays.asList(
    // 精确设置
    CheckerFactory.must(Date.class),
    // 子类设置
    CheckerFactory.assignable(List.class)
));
// 设置运行时白名单// 必须将该选项设置为 true
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
// 有白名单设置时, 则黑名单失效
QLExpressRunStrategy.addSecureMethod(RiskBean.class, "secureMethod");

  • ブラックリストを設定する
// 必须将该选项设置为 true
QLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);
// 这里不区分静态方法与成员方法, 写法一致
// 不支持重载, riskMethod 的所有重载方法都会被禁止
QLExpressRunStrategy.addSecurityRiskMethod(RiskBean.class, "riskMethod");

QLExpess によって現在デフォルトで追加されているブラックリストには次のものがあります。

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
  • 無限ループを防ぐ
//可通过timeoutMillis参数设置脚本的运行超时时间:1000ms
Object r = runner.execute(express, context, null, true, false, 1000);

ジェクスル

  • サンドボックスを使用する
// 使用中应该通过JexlSandbox的重载构造方法进行配置
new JexlBuilder().sandbox(new JexlSandbox()).create();

  • ホワイトリストの権限を設定する
new JexlBuilder().permissions(JexlPermissions.RESTRICTED.compose("com.jd.*")).create();

  • 機能スイッチ
// 关闭循环、new 实例,import等特性
new JexlBuilder().features(new JexlFeatures().loops(false).newInstance(false).importPragma(false)).create();

3.5 使用例

業界での使用状況から、さまざまな表現エンジンの実現可能性、エコロジー、統合、ベスト プラクティスについて学び、そこから学ぶことができます。以下の表からも分かるように、AviatorScript、MVEL、QLExpress はいずれも国内のビジネス分野でのユースケースがあり、記事を出力している企業もあり、参考になります。

名前 場合
アビエータースクリプト ライトフロー、JD スターリンク
MVEL 簡単なルール、コンパイルフロー、JD Starlink
OGNL マイバティス、ストラッツ
ゲーム
QLExpress コンパイルフロー、ライトフロー、アリババの社内ビジネスライン
ジェクスル 猫、ゼリー
宝石 JSP
ジャニノ Apache Spark、Apache Flink、Groovy

3.6 文法

理解して使いやすい構文により、開発効率が向上し、学習コストが削減されます。次に、型、演算子、制御ステートメント、コレクション、メソッド定義の観点から、さまざまな式エンジンの構文設計を比較します。

型に関しては、AviatorScript が独自の型を設計しているため、使用する際は型変換の優先順位がlong→bigint→decimal→doubleとなることに注意する必要があります。AviatorScript、MVEL、OGNL、および JEXL はすべて BigInteger および BigDecimal リテラルをサポートしています。つまり、リテラルを正確な計算に使用でき、より便利です。たとえば、これは BigDecimal リテラルを表します (AviatorScript の BigDecimal リテラルの接尾辞は M です) 10.24B。 。さらに、AviatorScript や QLExpress も高精度な計算設定をサポートしています。

演算子に関しては、QLExpress は置換演算子、カスタム演算子、および演算子エイリアスの追加をサポートしています。これらは複雑な式を簡素化したり、式をより直感的にするのに役立ちますが、プリセット関数を追加すると同様の効果が得られます。AviatorScript はカスタムの部分演算子もサポートしていますが、サポートの数は非常に限られています。AviatorScript、SpEl、および JEXL は、通常の一致演算子をサポートしています。

制御文に関しては、制御文をサポートしていない OGNL、SpEl、JUEL を除き、その他はすべてサポートしていますが、else ifAviatorScript の構文はやや特殊でelsif、foreach 文も異なりますので注意が必要です。ジャワ。

コレクションに関しては、JUEL を除くすべてのコレクションで簡単な定義メソッドが提供されていますが、構文が異なります。

関数定義に関しては、SpElとJUELはサポートしていませんが、OGNLは疑似ラムダ定義をサポートしており、その他は関数定義をサポートしています。QLExpress はラムダの定義をサポートしていません。

総合すると、Java 構文には多かれ少なかれ違いがあります。AviatorScript は独自の独自の構文を設計しているため、使用する前にその構文をよく理解しておく必要があります。QLExpress は、式をより直感的に見せるためのカスタム演算子をサポートしています。MVEL と JEXL の構文は Java に近いため、受け入れやすいかもしれません。OGNL、SpEl、JUEL の構文はより単純で、制御ステートメントや関数定義はサポートされていません。もちろん、より複雑な問題はいくつかの関数を事前設定することで解決できます。

4 選択のヒント

コミュニティに関しては、間違いなく SpEl が最も活発です。AviatorScript、QLExpress、MVEL は中国で非常に人気があり、QLExpress は Alibaba によって支持されています。

コード サイズと依存関係の点では、AviatorScript と MVEL の方が依存関係が少なく、コード サイズも小さくなります。

パフォーマンスの点では、式エンジンを使用してリテラル算術計算またはメソッド呼び出しを実行する場合は、SpEl または MVEL を選択できます。全体的なパフォーマンスを向上させたい場合は、AviatorScript を選択できます。

セキュリティの観点から、セキュリティ オプションをカスタマイズしたい場合は、AviatorScript、QLExpress、JEXL を検討できます。

ユースケースに関しては、AviatorScript、MVEL、QLExpress はすべて中国で実際のユースケースがあります。

文法に関しては主観的な要素もあるかもしれないので参考までにですが、個人的にはMVELやJEXLの方が使いやすい文法設計だと思います。

上記の側面の評価と分析を通じて、チームがそれぞれの状況や好みに基づいてプロジェクトに最適な Java 式エンジンを選択できるようにしたいと考えています。

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

ジャニノ: https://github.com/janino-compiler/janino

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

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

フェル: 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

著者: Jingdong Technology Feng Hao

出典: JD Cloud Developer Community 転送する場合は出典を明記してください

Alibaba Cloudが深刻な障害に見舞われ、全製品が影響(復旧) Tumblr がロシアのオペレーティングシステムAurora OS 5.0 を冷却新しいUIが公開 Delphi 12とC++ Builder 12、RAD Studio 12多くのインターネット企業がHongmengプログラマーを緊急採用UNIX時間17 億時代に突入しようとしている (すでに突入している) Meituan が兵力を募集し、Hongmeng システム アプリの開発を計画Amazon が Linux 上の .NET 8 への Android の依存を取り除くために Linux ベースのオペレーティング システムを開発独立した規模はFFmpeg 6.1「Heaviside」がリリースされまし
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10142679