SpEL 表达式语言基础
引言
在配置和注解里常看到 #{...} 的写法:@Value("#{...}")、@EventListener(condition = "...") 甚至缓存 key 模板。这是 Spring 表达式语言(SpEL)——在运行时对字符串表达式求值的统一能力。本章掌握 SpEL 的语法骨架与求值入口,够用即可,不必背全量语法。
核心概念
- 三件套(org.springframework.expression):
ExpressionParser(解析器,标准实现SpelExpressionParser)、Expression(解析结果,可反复求值)、EvaluationContext(求值上下文,提供根对象与变量)。 - 字面量与运算:字符串/数字/布尔、算术比较逻辑运算、三元、
?:(Elvis 缺省)、?.(安全导航,null 不炸)。 - 属性与方法访问:
user.name、list[0]、map['key']、name.toUpperCase()。 - 类型引用 T():
T(java.lang.Math).random()调静态方法/常量。 - 根对象与变量:求值上下文里的
#root、#this,setVariable("x", v)后用#x引用。 - Security 要点(官方推荐):处理不可信输入时用
SimpleEvaluationContext(只开放基本能力),别用功能全开的StandardEvaluationContext,后者允许通过 T() 触碰任意类,有安全风险。 - 常用落点:
@Value("#{...}")(也可${...}混合)、@EventListener(condition)、@Cacheable(key)等(以各模块官方文档为准)。
代码示例
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
public class SpelDemo {
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
// 基本求值:属性链 + 方法 + Elvis 缺省
Expression exp = parser.parseExpression("name?.toUpperCase() ?: 'unknown'");
SimpleEvaluationContext ctx = SimpleEvaluationContext
.forReadOnlyDataBinding().build();
ctx.setVariable("name", "spring"); // #name 变量
System.out.println(exp.getValue(ctx, String.class)); // SPRING
// T() 类型引用(SimpleEvaluationContext 下被限制,示例仅示意标准上下文场景)
Expression pi = new SpelExpressionParser()
.parseExpression("T(java.lang.Math).PI");
System.out.println(pi.getValue()); // 3.14159...
}
}
// 注解中的用法示例:
// @Value("#{systemProperties['user.name']}") String userName;
// @EventListener(condition = "#event.success") // 事件满足条件才触发
注意点
${...}是属性占位符(读配置),#{...}是 SpEL(求表达式),别混:@Value("${a.b}")vs@Value("#{...}")。- SpEL 在
@Value里求值发生在 Bean 初始化期,表达式抛错会让 Bean 创建失败。 - 对用户输入开放 SpEL = 潜在的表达式注入,官方明确建议不可信输入走
SimpleEvaluationContext或干脆禁用(以官方文档为准)。 - 表达式解析一次、可复用
Expression对象多次求值,别在热路径里反复 parse。 - 集合投影/选择(
![]/.?[])等高级语法按需查官方参考,不必一次记全。
小结
SpEL = 解析器 + 表达式 + 求值上下文。语法上记住“属性链、运算、Elvis/安全导航、T() 类型、#变量”就够覆盖日常;安全上牢记 SimpleEvaluationContext 与 StandardEvaluationContext 的分界线。它是 Spring 各模块“可配置逻辑”的公共底座。