TinyExpression v1.4.15 を Java プロジェクトに組み込む手順を説明します。
- Java 21+
- Maven 3.8+
pom.xml に依存関係を追加します:
<dependency>
<groupId>org.unlaxer</groupId>
<artifactId>tinyExpression</artifactId>
<version>1.4.15</version>
</dependency>テストや実行時に反射アクセスを使う場合は Surefire プラグインに add-opens を追加します:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
</argLine>
</configuration>
</plugin>式がコードと同梱されている場合や固定式の場合に使用します。
CalculationContext context = CalculationContext.newConcurrentContext();
context.set("age", 25);
context.set("gender", "male");newConcurrentContext() はスレッドセーフなコンテキストを返します。複数スレッドから安全に共有できます。
import org.unlaxer.tinyexpression.Source;
import org.unlaxer.tinyexpression.evaluator.javacode.JavaCodeCalculatorV3;
import org.unlaxer.tinyexpression.evaluator.javacode.SpecifiedExpressionTypes;
import org.unlaxer.tinyexpression.parser.ExpressionTypes;
PreConstructedCalculator calculator = new JavaCodeCalculatorV3(
new Source("if($age >= 20){100}else{0}"),
"AgeCheckCalc", // 生成クラス名(有効な Java 識別子)
new SpecifiedExpressionTypes(ExpressionTypes._float, ExpressionTypes._float),
Thread.currentThread().getContextClassLoader());JavaCodeCalculatorV3 はコンストラクタ時に式を Java バイトコードにコンパイルします。calculator インスタンスは構築後ステートレスなので、複数の apply() 呼び出しで再利用できます。
float result = ((Number) calculator.apply(context)).floatValue();
System.out.println(result); // 100.0テナントごとのルール管理、業務担当者による式の更新、依存関係制御付きパイプラインに使用します。
src/main/resources/formula-root/
69/formulaInfo.txt
tags:NORMAL
description:基本スコア
siteId:69
calculatorName:baseScore
var:baseScore
resultType:float
formula:
if($age >= 20){100}else{0}
---END_OF_PART---
tags:NORMAL
description:ボーナス
siteId:69
calculatorName:bonusScore
dependsOn:baseScore
var:finalScore
resultType:float
formula:
$baseScore + 10
---END_OF_PART---
dependsOn:baseScore により、baseScore が bonusScore より先に評価されることが保証されます。
FormulaInfoAdditionalFields fields = new FormulaInfoAdditionalFields(
"siteId", // パーティションキーフィールド名
info -> info.calculatorName); // 名前抽出関数: 式を識別する方法
// グローバルデフォルトバックエンドを設定(省略可能、既定値は JAVA_CODE)
fields.setExecutionBackend(ExecutionBackend.JAVA_CODE);FileBaseTinyExpressionInstancesCache cache = new FileBaseTinyExpressionInstancesCache(
Path.of("src", "main", "resources", "formula-root"),
fields);キャッシュはテナントごとに遅延ロード・コンパイルします。
ResultConsumer は各式の結果を受け取り、何をするかを決めます。
ResultConsumer resultConsumer = new ResultConsumer() {
@Override
public void accept(CalculationContext c, Calculator calculator, FormulaInfo info, Number result) {
// "var" キーで指定された変数名にコンテキストへ書き込み
info.getValue("var").ifPresent(name -> c.set(name, result));
}
@Override
public void accept(CalculationContext c, Calculator calculator, FormulaInfo info, String result) {
info.getValue("var").ifPresent(name -> c.set(name, result));
}
@Override
public void accept(CalculationContext c, Calculator calculator, FormulaInfo info, Boolean result) {
info.getValue("var").ifPresent(name -> c.set(name, result));
}
@Override
public void accept(CalculationContext c, Calculator calculator, FormulaInfo info, Object result) {
info.getValue("var").ifPresent(name -> c.setObject(name, result));
}
};CalculationContext ctx = CalculationContext.newConcurrentContext();
ctx.set("age", 30);
TinyExpressionsExecutor executor = new TinyExpressionsExecutor();
List<CalculationResult> results = executor.execute(
TenantID.create(69),
ctx,
resultConsumer,
cache,
Comparator.comparingInt(Calculator::dependsOnByNestLevel).reversed(), // 依存先から実行
calculator -> true, // 全式を対象に
Thread.currentThread().getContextClassLoader());
System.out.println("実行数: " + results.size()); // 2
System.out.println("finalScore: " + ctx.get("finalScore")); // 110.0本番環境の推奨設定:
- グローバルデフォルトを
JAVA_CODEに設定(既定値のまま) - 必要な場合のみ式ごとに上書き(例:
backend:P4_AST_EVALUATOR) - 本番切り替え前にパリティテストを実行
バックエンドの詳細な比較は docs/backends.md を参照。
式内から Java メソッドを呼び出すには:
- Java クラスにメソッドを実装
package myapp;
import org.unlaxer.tinyexpression.CalculationContext;
public class RiskChecker {
public boolean isHighRisk(CalculationContext context, String region) {
return "HIGH_RISK".equals(region);
}
}- 実行前にコンテキストへオブジェクトを登録
context.set(new myapp.RiskChecker());- 式内で参照
import myapp.RiskChecker#isHighRisk as isHighRisk;
if(external returning as boolean isHighRisk($region)){100}else{0}