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

发布于:2025-07-30 ⋅ 阅读:(20) ⋅ 点赞:(0)

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);
    // 100
   
    
    Expression 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 能显著提升开发灵活性,尤其在需要动态配置的场景中(如规则引擎、个性化配置),但务必警惕安全风险,做好防护措施。


网站公告

今日签到

点亮在社区的每一天
去签到