SpEL
写 Spring 的时候,经常会看到一些看起来像小型脚本的写法,比如 #{...}、@Value("#{systemProperties['user.home']}")、@PreAuthorize("hasRole('ADMIN')")。这些东西看起来不像普通 Java 代码,但又确实在项目里经常出现。
这些表达式背后用到的,就是 SpEL。它的全称是 Spring Expression Language,也就是 Spring 表达式语言。简单说,它是 Spring 提供的一套表达式机制,用来在运行时读取数据、调用方法、做条件判断。
为什么会有 SpEL
如果一个框架只支持写死配置,那很多动态场景都会很难处理。
比如下面这些需求:
- 从配置、Bean 或上下文里动态取值
- 根据条件决定某个 Bean 是否生效
- 在方法执行前做权限判断
- 在缓存、消息、调度等场景里写一些简单规则
这些场景如果全部都写成 Java 代码,当然不是不行,但很多时候会显得比较重。SpEL 的作用,就是给 Spring 提供一套统一的表达式能力,让这些动态逻辑可以用更轻量的方式写出来。
SpEL 是什么
按照 Spring Framework 官方文档的说法,SpEL 是一套支持在运行时查询和操作对象图的表达式语言。
如果把这句话说得直接一点,可以把它理解成 Spring 里的“表达式语法层”。它允许你在不写完整 Java 逻辑的情况下,完成下面这些事:
- 访问对象属性
- 调用对象方法
- 使用逻辑运算符和条件表达式
- 访问集合、Map、数组
- 引用 Bean
- 做集合筛选和投影
也就是说,SpEL 并不是一门独立语言,它更像 Spring 提供的一套运行时表达式能力。
SpEL 能做什么
读取属性和调用方法
这是最基础的用法。比如可以直接访问对象属性、Map 值,或者调用对象方法:
1 | ExpressionParser parser = new SpelExpressionParser(); |
这一类表达式适合做一些简单值计算,不适合写太复杂的业务逻辑。
条件判断
SpEL 支持常见的逻辑判断和三元表达式,所以可以很方便地写条件选择:
1 |
|
这种写法在配置或者注解参数里比较常见。
集合筛选和投影
这是 SpEL 比较有特点的地方。它支持对集合做筛选和投影。
比如:
- 选择满足条件的元素
- 从对象集合里取某个字段组成新集合
官方文档里把这部分称为 collection selection 和 collection projection。这个能力很方便,但表达式一长,可读性会下降得很快。
在 Spring 里常见的使用场景
SpEL 真正常见,不是在单独写 ExpressionParser 的时候,而是在各种 Spring 注解和配置场景里。
@Value
这是很多人最早接触 SpEL 的地方。除了读配置项,也可以直接写表达式:
1 |
|
或者从系统属性、Bean 属性里取值:
1 |
|
权限表达式
在 Spring Security 里,很多权限注解背后也和表达式机制有关。例如:
1 |
|
这类写法在项目里非常常见。虽然平时大家更习惯把它当成权限语法来看,但本质上它也是表达式求值的一种体现。
条件装配
Spring 里有些场景也会用表达式来控制 Bean 是否生效,比如 @ConditionalOnExpression:
1 |
这种方式适合写简单条件,但如果条件本身已经比较复杂,就不太建议继续堆表达式了。
使用 SpEL 时要注意什么
SpEL 很方便,但也不是越多越好。
表达式太长会影响可读性
短表达式读起来很顺,长表达式就容易变成“配置里写业务逻辑”。一旦出现多层判断、嵌套调用、复杂集合操作,后面维护起来会比较痛苦。
适合写轻逻辑,不适合替代业务代码
SpEL 更适合做动态取值、简单条件、权限判断这类事情。如果已经开始写复杂流程判断,那通常应该回到 Java 代码本身。
不要把不可信输入直接当表达式执行
SpEL 本身有很强的表达能力,所以如果把外部输入直接交给表达式引擎处理,会带来明显的安全风险。正常项目里,一般不会让用户自己拼接和执行表达式。
小结
SpEL 可以理解成 Spring 里的表达式工具层。它的价值不在于替代 Java,而在于把一些运行时的小型动态逻辑写得更轻。
像 @Value、权限表达式、条件装配这些场景,用 SpEL 会很自然;但如果表达式已经开始承载太多业务逻辑,那通常就说明这部分代码应该换个地方写了。
参考资料
- Spring Framework Reference - Spring Expression Language: https://docs.spring.io/spring-framework/reference/core/expressions.html
- Spring Framework Reference - SpEL Language Reference: https://docs.spring.io/spring-framework/reference/core/expressions/language-ref.html