如何在运行时动态评估来自JSON数据的可变Java表达式?
Java动态评估字符串三元表达式的实现方案
1. 使用Java内置ScriptEngine(无额外依赖)
Java自带的ScriptEngine可以直接执行兼容Java语法的表达式,步骤简单无需引入第三方库:
- 获取JavaScript引擎实例(Java 8默认用Nashorn,Java 11+推荐GraalVM JS引擎)
- 将运行时变量绑定到引擎上下文
- 执行表达式并获取结果
代码示例:
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.util.Map; public class ScriptEngineEvaluator { public static Object evaluate(String expression, Map<String, Object> variables) throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("js"); // 绑定运行时变量 variables.forEach(engine::put); // 执行表达式 return engine.eval(expression); } public static void main(String[] args) throws ScriptException { String expr = "value1 < 0 ? 3:5"; Map<String, Object> vars = Map.of("value1", -2); Object result = evaluate(expr, vars); System.out.println(result); // 输出3 } }
注意:Java 11及以后Nashorn被标记为废弃,需确保GraalVM JS模块可用;高频调用场景要注意引擎的执行开销。
2. 使用Apache Commons JEXL库(推荐复杂/可变场景)
Apache Commons JEXL是专门为动态表达式求值设计的工具,语法与Java高度兼容,扩展性强,适合表达式未来可能变更的场景。
步骤:
- 引入Maven依赖:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-jexl3</artifactId> <version>3.3</version> </dependency>
- 编写求值代码:
import org.apache.commons.jexl3.JexlBuilder; import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.JexlExpression; import org.apache.commons.jexl3.MapContext; import java.util.Map; public class JexlEvaluator { private static final JexlEngine JEXL_ENGINE = new JexlBuilder().create(); public static Object evaluate(String expression, Map<String, Object> variables) { // 编译表达式(可缓存编译结果提升重复调用性能) JexlExpression expr = JEXL_ENGINE.createExpression(expression); MapContext context = new MapContext(); variables.forEach(context::set); return expr.evaluate(context); } public static void main(String[] args) { String expr = "value1 < 0 ? 3:5"; Map<String, Object> vars = Map.of("value1", 4); Object result = evaluate(expr, vars); System.out.println(result); // 输出5 } }
优势:支持复杂表达式(逻辑运算、函数调用等),编译后的表达式可复用,适配未来表达式的扩展需求。
3. 自定义简单解析器(仅适用于固定格式三元表达式)
如果表达式始终是条件 ? 真分支 : 假分支的极简格式,可自己拆分表达式求值,避免依赖,但局限性极大,表达式格式变更后需同步修改解析逻辑:
代码示例:
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.util.Map; public class SimpleTernaryEvaluator { public static Object evaluate(String expression, Map<String, Object> variables) { // 拆分最外层的三元运算符(跳过括号内的内容) int questionMarkIdx = findOuterOperator(expression, '?'); int colonIdx = findOuterOperator(expression.substring(questionMarkIdx + 1), ':') + questionMarkIdx + 1; String conditionStr = expression.substring(0, questionMarkIdx).trim(); String trueBranch = expression.substring(questionMarkIdx + 1, colonIdx).trim(); String falseBranch = expression.substring(colonIdx + 1).trim(); // 评估条件 boolean condition = evaluateCondition(conditionStr, variables); // 返回对应分支结果 return condition ? parseBranch(trueBranch, variables) : parseBranch(falseBranch, variables); } private static boolean evaluateCondition(String condition, Map<String, Object> variables) { try { ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); variables.forEach(engine::put); return (boolean) engine.eval(condition); } catch (ScriptException e) { throw new RuntimeException("条件解析失败", e); } } private static Object parseBranch(String branch, Map<String, Object> variables) { if (variables.containsKey(branch)) { return variables.get(branch); } // 尝试解析数字常量 try { return Integer.parseInt(branch); } catch (NumberFormatException e) { try { return Double.parseDouble(branch); } catch (NumberFormatException ex) { throw new RuntimeException("分支解析失败", ex); } } } // 找到最外层的运算符(忽略括号内的) private static int findOuterOperator(String str, char operator) { int bracketCount = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c == '(') bracketCount++; else if (c == ')') bracketCount--; else if (c == operator && bracketCount == 0) { return i; } } throw new IllegalArgumentException("找不到合法的" + operator + "运算符"); } public static void main(String[] args) { String expr = "value1 < 0 ? 3:5"; Map<String, Object> vars = Map.of("value1", -1); Object result = evaluate(expr, vars); System.out.println(result); // 输出3 } }
注意:此方式仅适合极简场景,表达式一旦引入复杂逻辑(如括号、&&/||运算),解析逻辑会急剧复杂化。
内容的提问来源于stack exchange,提问作者Brian Figueroa
相关产品推荐
相关产品推荐

