如何识别触发规则的Spring SpEL表达式具体部分?
如何识别SpEL表达式中触发规则的具体部分?
问题描述
我想知道有没有办法识别SpEL表达式中触发规则的具体部分?比如对于表达式a < 10 || a > 20,希望明确规则触发是因为a < 10成立,还是a > 20成立。目前我只能通过以下代码判断整个表达式的真假,但无法定位具体触发的子表达式:
ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(rule.getExpression()); return Boolean.TRUE.equals(expression.getValue(evaluationContext, Boolean.class));
可行解决方案
1. 遍历SpEL抽象语法树(AST)节点逐个评估
SpEL解析后的Expression本质是一棵抽象语法树,你可以通过getAST()获取根节点,然后针对逻辑运算符(如OpOr、OpAnd)遍历子节点,逐个评估结果来定位触发部分:
ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("a < 10 || a > 20"); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("a", 8); // 示例输入值 SpelNode rootNode = expression.getAST(); if (rootNode instanceof OpOr) { SpelNode leftSubExpr = rootNode.getChild(0); SpelNode rightSubExpr = rootNode.getChild(1); boolean leftResult = Boolean.TRUE.equals(leftSubExpr.getValue(context, Boolean.class)); boolean rightResult = Boolean.TRUE.equals(rightSubExpr.getValue(context, Boolean.class)); if (leftResult) { System.out.println("触发规则的部分是: " + leftSubExpr.toStringAST()); } else if (rightResult) { System.out.println("触发规则的部分是: " + rightSubExpr.toStringAST()); } }
这种方法需要针对不同逻辑运算符做适配,适合结构相对固定的表达式。
2. 拆分表达式字符串后独立评估
如果你的表达式是由||或&&直接连接的扁平子表达式(无嵌套括号),可以直接拆分原表达式字符串,逐个评估子表达式:
String originalExpr = "a < 10 || a > 20"; String[] subExprs = originalExpr.split("\\|\\|"); // 转义正则特殊字符 EvaluationContext context = new StandardEvaluationContext(); context.setVariable("a", 25); ExpressionParser parser = new SpelExpressionParser(); for (String subExpr : subExprs) { boolean result = Boolean.TRUE.equals(parser.parseExpression(subExpr.trim()).getValue(context, Boolean.class)); if (result) { System.out.println("触发规则的部分是: " + subExpr.trim()); break; // 对于||逻辑,找到第一个成立的子表达式即可停止 } }
这种方式实现简单,但不适用于包含嵌套逻辑的表达式(比如(a <10 && b>5) || c>20)。
3. 自定义评估监听记录触发节点
通过实现EvaluationListener,在SpEL评估过程中跟踪每个子节点的结果,从而记录触发的子表达式:
class TrackingEvaluationListener implements EvaluationListener { private final List<String> triggeredSubExprs = new ArrayList<>(); @Override public void afterEvaluation(ExpressionNode node, EvaluationContext context, Object result) { // 仅记录返回true的非根节点子表达式 if (node instanceof SpelNode && Boolean.TRUE.equals(result) && node.getParent() != null) { triggeredSubExprs.add(((SpelNode) node).toStringAST()); } } public List<String> getTriggeredSubExprs() { return triggeredSubExprs; } } // 使用示例 ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("a < 10 || a > 20"); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("a", 5); TrackingEvaluationListener listener = new TrackingEvaluationListener(); ((SpelExpression) expression).evaluate(context, listener); System.out.println("触发规则的部分是: " + listener.getTriggeredSubExprs());
这种方案灵活性最高,能处理复杂嵌套的表达式,但需要对SpEL的内部评估逻辑有一定了解。
内容的提问来源于stack exchange,提问作者marojbor
相关产品推荐
相关产品推荐

