Spring Expression Language (SpEL) 完全指南
目录
- SpEL 基础语法
- SpEL 操作符
- 集合与数组操作
- 变量与根对象
- 类型相关操作
- 在 Spring 中的应用场景
- 属性占位符
${}
与 SpEL#{}
的区别 - Property Placeholder 配置
- 高级特性与注意事项
- 总结
1. SpEL 基础语法
SpEL 是 Spring 的动态表达式语言,用于运行时查询和操作对象。表达式以 #{ }
包裹。
1.1 字面量表达式
@Value("#{3.14}") // 注入数字
private double pi;
@Value("#{'Hello'}") // 注入字符串
private String message;
@Value("#{true}") // 布尔值
private boolean isActive;
1.2 引用 Bean 和属性
@Component("userService")
public class UserService {
public String getUsername() { return "Alice"; }
}
// 引用 Bean 的方法
@Value("#{userService.username}")
private String username; // 输出 "Alice"
1.3 调用静态方法
@Value("#{T(java.lang.Math).PI}")
private double pi; // 注入 Math.PI
@Value("#{T(java.util.UUID).randomUUID()}")
private String uuid; // 生成 UUID
2. SpEL 操作符
2.1 算术与比较操作符
@Value("#{1 + 2 * 3}") // 7
private int sum;
@Value("#{user.age > 18}") // true/false user指的是根对象root的user属性,即IOC容器中某个名为user的bean
private boolean isAdult;
2.2 逻辑与三元操作符
@Value("#{isAdmin && isActive}")
private boolean isValid;
@Value("#{user.age > 18 ? 'Adult' : 'Child'}")
private String category;
2.3 Elvis 操作符(默认值)
@Value("#{user.nickname ?: 'Anonymous'}")
private String nickname; // 若 nickname 为 null,返回 'Anonymous'
2.4 安全导航操作符
@Value("#{user?.address?.city}")
private String city; // 避免 NullPointerException
3. 集合与数组操作
3.1 访问元素
@Value("#{list[0]}") // 列表第一个元素
private String firstElement;
@Value("#{map['key']}") // Map中key的值
private String value;
3.2 集合过滤(Selection)
@Value("#{users.?[age > 18]}")
private List<User> adults; // 过滤年龄 >18 的用户
3.3 集合投影(Projection)
@Value("#{users.![name]}")
private List<String> names; // 提取所有用户的名字
4. 变量与根对象
4.1 变量定义
通过 EvaluationContext
动态设置变量:
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("max", 100);
ExpressionParser parser = new SpelExpressionParser();
boolean result = parser.parseExpression("#max > 50").getValue(context, Boolean.class); // true
4.2 根对象(Root Object)
- 定义:表达式默认操作的上下文对象。
- 显式引用:通过
#root
访问。
User rootUser = new User("Alice", 25);
EvaluationContext context = new StandardEvaluationContext(rootUser);
// 直接访问根对象属性
String name = parser.parseExpression("name").getValue(context, String.class); // "Alice"
4.3 变量与根对象的区别
特性 | 根对象 | 变量 |
---|---|---|
访问方式 | 隐式(如 name )或 #root | 显式(如 #variable ) |
设置方式 | 通过 EvaluationContext 构造函数或方法 | context.setVariable() |
5. 类型相关操作
5.1 类型判断
@Value("#{user instanceof T(com.example.User)}")
private boolean isUserType;
5.2 构造对象
@Value("#{new com.example.User('Bob', 30)}")
private User newUser; // 需全限定类名
6. 在 Spring 中的应用场景
6.1 @Value
注解注入
@Value("#{systemProperties['user.timezone']}")
private String timeZone;
6.2 XML 配置
<bean id="config" class="com.example.Config">
<property name="max" value="#{T(java.lang.Math).random() * 100}"/>
</bean>
6.3 Spring Security
@PreAuthorize("hasRole('ADMIN') or #user.id == authentication.principal.id") // 这里的根对象是什么呢?
public void updateUser(User user) { ... }
7. 属性占位符 ${}
vs SpEL #{}
7.1 ${}
:读取 .properties
文件
# application.properties
app.name=MyApp
@Value("${app.name}")
private String appName; // 注入 "MyApp"
@Value("${app.timeout:3000}") // 默认值 3000
private int timeout;
7.2 混合使用
@Value("#{'Server: ' + ${app.name}}")
private String serverInfo; // 输出 "Server: MyApp"
8. Property Placeholder 配置
8.1 XML 配置
<context:property-placeholder location="classpath:app.properties"/>
8.2 Java 配置(非 Spring Boot)
@Configuration
@PropertySource("classpath:app.properties")
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
8.3 Spring Boot 自动配置
默认加载 application.properties
,无需额外配置。
9. 高级特性与注意事项
9.1 自定义函数
// 注册自定义方法
Method method = MathUtils.class.getMethod("square", double.class);
context.registerFunction("square", method);
// 表达式调用
@Value("#{#square(10)}")
private double result;
9.2 安全性
// 禁用编译器以提高安全性
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.OFF, null);
ExpressionParser parser = new SpelExpressionParser(config);
9.3 性能优化
- SpEL 首次执行后会编译为字节码,接近原生性能。
- 避免在频繁调用的代码中使用复杂表达式。
10. 总结
- SpEL 核心能力:动态计算、集合操作、安全导航。
${}
与#{}
区别:前者读取属性,后者执行表达式。- 根对象:默认上下文对象,可通过
#root
显式访问。 - 应用场景:动态配置、安全校验、条件注入。