如何调用Java中以字符串形式给出的链式方法调用表达式?
实现方案
你可以通过以下三种主流方式实现该需求,按照易用性从高到低排序:
方案1:使用Spring SpEL(推荐,Spring项目首选)
Spring自带的表达式语言原生支持对象链式调用解析,无需额外引入依赖,性能和稳定性都经过生产验证。
import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; // 解析器全局单例即可,避免重复创建开销 private final ExpressionParser spelParser = new SpelExpressionParser(); public String getKey(String key) { StandardEvaluationContext context = new StandardEvaluationContext(); // 把当前的order对象注入到表达式上下文 context.setVariable("order", order); try { Object result = spelParser.parseExpression(key).getValue(context); return result != null ? result.toString() : null; } catch (Exception e) { // 自行处理表达式解析、执行异常 e.printStackTrace(); return null; } }
如果你的配置项去掉了order.前缀,代码可以进一步简化:
Object result = spelParser.parseExpression(key).getValue(order);
方案2:使用OGNL(无Spring依赖场景)
OGNL是专门用于对象图导航的表达式工具,非常适合该场景,需要单独引入依赖:
<!-- Maven依赖 --> <dependency> <groupId>ognl</groupId> <artifactId>ognl</artifactId> <version>3.3.4</version> </dependency>
代码实现:
import ognl.Ognl; import ognl.OgnlContext; public String getKey(String key) { OgnlContext context = new OgnlContext(); context.put("order", order); try { Object expression = Ognl.parseExpression(key); Object result = Ognl.getValue(expression, context, context.getRoot()); return result != null ? result.toString() : null; } catch (Exception e) { e.printStackTrace(); return null; } }
方案3:纯反射实现(无第三方依赖场景)
适合不想引入额外依赖的简单场景,以下是简化实现,可根据自己的需求扩展参数类型支持:
import java.lang.reflect.Method; import java.util.regex.Matcher; import java.util.regex.Pattern; // 匹配带参数的方法,例如get(0) private static final Pattern METHOD_PATTERN = Pattern.compile("(\\w+)\\((.*?)\\)"); public String getKey(String key) { // 去掉前缀order. String path = key.substring(key.indexOf(".") + 1); String[] segments = path.split("\\."); Object current = order; try { for (String segment : segments) { Matcher matcher = METHOD_PATTERN.matcher(segment); if (matcher.matches()) { // 处理带参数的方法 String methodName = matcher.group(1); int param = Integer.parseInt(matcher.group(2)); Method method = current.getClass().getMethod(methodName, int.class); current = method.invoke(current, param); } else { // 处理无参getter Method method = current.getClass().getMethod(segment); current = method.invoke(current); } if (current == null) return null; } return current.toString(); } catch (Exception e) { e.printStackTrace(); return null; } }
注意事项
- 生产环境使用建议增加表达式缓存,避免每次请求重复解析表达式带来的性能损耗
- 所有方案都建议增加空指针、类型转换的异常捕获,避免链式调用中间节点为空导致服务报错
- 如果配置项可被外部修改,必须增加表达式白名单校验,避免恶意表达式执行敏感操作
内容的提问来源于stack exchange,提问作者Atihska
相关产品推荐
相关产品推荐

