当前位置: 首页 > news >正文

深入解析 Spring SpEL:SpelExpressionParser 的使用与实践

01 引言

前一段时间看到JetCache框架的源码中使用了Spring SpEL,回想到之前看到某框架的源码时,也看到类似的技术。于是自己也试着学习一下。

我们一直在借助框架使用着SpEL技术,但是可能却不能单独拎出来使用过。Spring中有很多实用的工具类,我们可以直接使用。

今天我们一起看看SpEL的表达式的用法。

02 基本概念

Spring 表达式语言(SpEL)是一种强大的表达式解析框架,集成于 Spring 生态中,用于在运行时动态计算表达式值。SpelExpressionParserSpEL 的核心解析器,能够将字符串表达式转换为可执行的 Expression 对象,支持对象属性访问、方法调用、运算符操作、集合处理等复杂逻辑。

典型应用场景

  • Spring 注解(如 @Value
  • Spring Security 权限表达式
  • XML/注解配置的动态参数
  • 规则引擎、动态公式计算
  • 模板渲染(如 Thymeleaf)

官网地址:https://docs.spring.io/spring-framework/reference/core/expressions.html

03 案例

3.1 基础使用案例

解析简单的表达式。

@Test
void test01() {ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression("'Hello World'.concat('!')");String message = (String) exp.getValue();System.out.println(message);// Hello World!Expression intExp = parser.parseExpression("25 * 4");Integer message = (Integer) intExp.getValue();System.out.println(message);// 100Expression boolExp = parser.parseExpression("100 > 50 && true");System.out.println(boolExp.getValue()); // 输出: true
}

简单来说,就是将一个字符串解析成对应的运算或者方法调用。

3.2 对象属性访问与方法调用

通过上下文访问对象或者调用对象的方法

@Test
void test02() {User user = new User("Alice", 30);ExpressionParser parser = new SpelExpressionParser();// 创建上下文并绑定对象EvaluationContext context = new StandardEvaluationContext(user);// 访问属性Expression nameExp = parser.parseExpression("name");// 从上下文中获取对象属性System.out.println(nameExp.getValue(context)); // 输出: Alice// 调用方法Expression methodExp = parser.parseExpression("setName('Bob')");methodExp.getValue(context); // 执行setName方法System.out.println(user.getName()); // 输出: Bob// 链式操作Expression chainExp = parser.parseExpression("name.toUpperCase().length()");System.out.println(chainExp.getValue(context)); // 输出: 3 (BOB的长度) 
}

绑定上下文有两种方式:

  • 构造函数:new StandardEvaluationContext(user)
  • Setter方法:new StandardEvaluationContext().setVariable("user", user);

两种方式的取值各不相同:

  • 构造函数取值:直接获取属性parser.parseExpression("name");
  • Setter方法取值:通过#号调用parser.parseExpression("#user.name");

3.3 集合的操作

SpEL 支持对集合进行过滤、投影等操作:

@Test
void test03() {List<User> users = Arrays.asList(new User("Alice", 25),new User("Bob", 30),new User("Charlie", 20));EvaluationContext context = new StandardEvaluationContext();context.setVariable("users", users);ExpressionParser parser = new SpelExpressionParser();// 过滤年龄>25的用户Expression filterExp = parser.parseExpression("#users.?[age > 25]");List<User> filtered = (List<User>) filterExp.getValue(context);filtered.forEach(u -> System.out.println(u.getName())); // 输出: Bob// 提取所有用户名Expression mapExp = parser.parseExpression("#users.![name.toUpperCase()]");List<String> names = (List<String>) mapExp.getValue(context);System.out.println(names); // 输出: [ALICE, BOB, CHARLIE]
}

值得说明的是:提取集合的某个属性时,用到的!,过滤或者取值用到?

04 使用注意事项

4.1 安全风险

3.2案例中,可以看到:我们是可以随意修改上下文变量的值的。如果担心被人恶意篡改上下文参数,我们需要使用org.springframework.expression.spel.support.SimpleEvaluationContext的限制功能(默认禁用类型构造、方法调用):

SimpleEvaluationContext simpleContext = SimpleEvaluationContext.forReadOnlyDataBinding().build();
exp.getValue(simpleContext); // 安全模式

再次修改参数就是出现下面的报错:

4.2 空值的处理

NPE是每一个程序员都会遇到的异常,我们看看Spring SpEL如何处理:使用 ?. 安全导航操作符避免 NPE

EvaluationContext context = new StandardEvaluationContext();
context.setVariable("book", null);ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("#book?.bookName");// 返回 null,不会出现NPE

4.3 类型转化

明确指定返回值类型:

String name = exp.getValue(context, String.class);

05 小结

SpelExpressionParser 是 Spring 生态中处理动态表达式的利器,通过简洁的语法实现了:

  • 对象属性/方法动态访问
  • 复杂集合操作
  • 与 Spring 配置深度集成

掌握 SpEL 能显著提升开发灵活性,尤其在需要动态配置的场景中(如规则引擎、个性化配置),但务必警惕安全风险,做好防护措施。

http://www.dtcms.com/a/305134.html

相关文章:

  • Python游戏开发:Pygame全面指南与实战
  • JAVA存储原生json字符串到redis,去除@class,实现原生命令操作教程
  • 从传统到智能:Midscene.js 如何用 AI 颠覆自动化测试!
  • 【Lua】题目小练4
  • 深入解析RocksDB的MVCC和LSM Tree level
  • 基于springboot/java/VUE的旅游管理系统/旅游网站的设计与实现
  • USB Type-C PD协议一文通
  • mangoDB面试题及详细答案 117道(026-050)
  • CVE-2021-1675
  • 【C语言进阶】题目练习
  • docker部署zingerbee/netop 轻量级网络流量监控工具
  • 河南萌新联赛2025第(二)场:河南农业大学(补题)
  • 高端医疗超声AFE模拟前端应用
  • 机器学习之线性回归——小白教学
  • 关于为什么写分配法搭配写回法?非写分配法搭配全写法?
  • python基础:request请求查询参数的基本使用、携带请求参数的两种方法、 json串和python中数据类型转化、 post模拟登录
  • 全方位Python学习方法论:从入门到精通的系统指南
  • GB/T 4706.1-2024 家用和类似用途电器的安全 第1部分:通用要求 与2005版差异(21)
  • 【Spring】日志级别的分类和使用
  • 计算机视觉-局部图像描述子
  • 代理IP轮换机制:突破反爬虫的关键策略
  • AI驱动的知识管理新时代:释放组织潜力的关键武器
  • win10 环境删除文件提示文件被使用无法删除怎么办?
  • MPLS 专线网络
  • 字符集学习
  • 实现多路标注截图
  • GESP2025年6月认证C++七级( 第三部分编程题(1)线图)
  • Spring Boot中的this::语法糖详解
  • Spring与数学的完美碰撞
  • 偏二甲肼气体浓度报警控制系统