SpEL 表达式使用
简介
Java SpEL(Spring Expression Language) 是 Spring 框架提供的一种强大的表达式语言,它支持在运行时查询和操作对象图。SpEL 可以用于 XML 配置、注解配置以及代码中。下面详细介绍 SpEL 的基本用法和常见表达式。
SpEL 基础
SpEL 表达式以 #{} 的形式包围,可以在其中使用各种运算符、函数、变量等。
字面量表达式
-
字符串:
#{'Hello World'} -
数字:
#{3.14} -
布尔值:
#{true}
引用Bean、属性和方法
-
引用Bean:
#{beanName} -
引用Bean的属性:
#{beanName.property} -
调用Bean的方法:
#{beanName.method()}
运算符
-
算术运算符:
+,-,*,/,%,^ -
关系运算符:
==,!=,<,>,<=,>= -
逻辑运算符:
and,or,not -
条件运算符:
?:(三元运算符),?:(Elvis运算符) -
正则表达式:
matches
集合操作
-
访问列表:
#{list[0]} -
访问映射:
#{map['key']} -
集合投影:
#{list.![property]}(从集合中每个元素提取属性形成新列表) -
集合选择:
#{list.?[condition]}(根据条件过滤集合)
变量
可以使用 #variableName 来引用上下文中的变量,例如在 Spring Security 中常用 hasRole('ROLE_ADMIN') 等
在 XML 配置中使用
在 Spring 的 XML 配置文件中,可以使用 SpEL 来注入值。
<bean id="myBean" class="com.example.MyBean">
<property name="value" value="#{systemProperties['user.timezone']}"/>
</bean>
在注解中使用
在 Spring 的注解中,例如 @Value,可以使用 SpEL。
@Value("#{systemProperties['user.home']}")
private String userHome;
在 Spring Security 中使用
Spring Security 中经常使用 SpEL 来定义访问控制。
@PreAuthorize("hasRole('ADMIN')")
public void someMethod() {
// ...
}
在代码中直接使用
可以通过 SpelExpressionParser 来在代码中解析 SpEL 表达式。
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
SpEL 的基本使用
SpEL 字面量表达式
// 字符串
parser.parseExpression("'Hello World'").getValue();
// 数字
parser.parseExpression("3.14159").getValue();
// 布尔值
parser.parseExpression("true").getValue();
// null
parser.parseExpression("null").getValue();
数学运算表达式
// 加法
parser.parseExpression("2 + 3").getValue(); // 5
// 减法
parser.parseExpression("5 - 2").getValue(); // 3
// 乘法
parser.parseExpression("4 * 3").getValue(); // 12
// 除法
parser.parseExpression("10 / 2").getValue(); // 5
// 取模
parser.parseExpression("10 % 3").getValue(); // 1
// 幂运算
parser.parseExpression("2 ^ 3").getValue(); // 8
关系表达式
// 等于
parser.parseExpression("2 == 2").getValue(); // true
// 不等于
parser.parseExpression("2 != 3").getValue(); // true
// 大于小于
parser.parseExpression("5 > 3").getValue(); // true
parser.parseExpression("5 < 3").getValue(); // false
// 实例检查
parser.parseExpression("'abc' instanceof T(String)").getValue(); // true
逻辑表达式
// 与运算
parser.parseExpression("true and false").getValue(); // false
// 或运算
parser.parseExpression("true or false").getValue(); // true
// 非运算
parser.parseExpression("!false").getValue(); // true
条件表达式
// 三元运算符
parser.parseExpression("2 > 1 ? 'yes' : 'no'").getValue(); // "yes"
// Elvis 运算符(简化三元)
parser.parseExpression("name?:'Unknown'").getValue(context); // 如果name为null返回'Unknown'
正则表达式
parser.parseExpression("'123' matches '\\d+'").getValue(); // true
对象操作表达式
属性访问
// 创建测试类
class User {
private String name;
private int age;
private List<String> hobbies;
// getters and setters
}
User user = new User("John", 25, Arrays.asList("reading", "swimming"));
// 属性访问
StandardEvaluationContext context = new StandardEvaluationContext(user);
parser.parseExpression("name").getValue(context); // "John"
parser.parseExpression("age").getValue(context); // 25
// 安全导航(避免NullPointerException)
parser.parseExpression("address?.city").getValue(context); // 如果address为null返回null
方法调用
parser.parseExpression("getName()").getValue(context);
parser.parseExpression("setName('Mike')").getValue(context);
parser.parseExpression("'hello'.toUpperCase()").getValue(); // "HELLO"
类型操作
// 调用静态方法
parser.parseExpression("T(java.lang.Math).random()").getValue();
// 访问静态字段
parser.parseExpression("T(java.lang.Math).PI").getValue();
// 实例化对象
parser.parseExpression("new java.util.Date()").getValue();
集合操作表达式
数组和列表操作
List<String> list = Arrays.asList("a", "b", "c");
context.setVariable("list", list);
// 访问元素
parser.parseExpression("#list[0]").getValue(context); // "a"
// 投影(提取属性)
List<User> users = Arrays.asList(new User("John", 25), new User("Mike", 30));
context.setVariable("users", users);
List<String> names = (List<String>) parser.parseExpression("#users.![name]").getValue(context);
// ["John", "Mike"]
// 选择(过滤)
List<User> youngUsers = (List<User>) parser.parseExpression("#users.?[age < 30]").getValue(context);
Map 操作
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
context.setVariable("map", map);
parser.parseExpression("#map['key1']").getValue(context); // "value1"
parser.parseExpression("#map.key2").getValue(context); // "value2"
高级特性
变量定义和使用
context.setVariable("myVar", "Hello");
parser.parseExpression("#myVar + ' World'").getValue(context); // "Hello World"
函数注册
// 自定义函数
public class StringUtils {
public static String reverse(String input) {
return new StringBuilder(input).reverse().toString();
}
}
// 注册函数
context.registerFunction("reverse",
StringUtils.class.getDeclaredMethod("reverse", String.class));
parser.parseExpression("#reverse('hello')").getValue(context); // "olleh"
集合投影和选择
List<User> users = Arrays.asList(
new User("John", 25, Arrays.asList("reading", "swimming")),
new User("Mike", 30, Arrays.asList("gaming", "coding"))
);
context.setVariable("users", users);
// 投影 - 提取所有用户名
List<String> names = (List<String>) parser.parseExpression("#users.![name]").getValue(context);
// 选择 - 过滤年龄大于25的用户
List<User> filtered = (List<User>) parser.parseExpression("#users.?[age > 25]").getValue(context);
// 嵌套投影
List<List<String>> allHobbies = (List<List<String>>)
parser.parseExpression("#users.![hobbies]").getValue(context);
在 Spring 配置中使用
@Component
public class AppConfig {
@Value("#{systemProperties['java.version']}")
private String javaVersion;
@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
@Value("#{userService.defaultUser.name}")
private String defaultUserName;
}
在 Spring Security 中使用
@PreAuthorize("hasRole('ADMIN') or #user.name == authentication.name")
public void updateUser(User user) {
// 方法实现
}
在 Spring Data 中使用
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.name = ?#{[0]} AND u.age = ?#{[1]}")
List<User> findByNameAndAge(String name, int age);
}
安全考虑
使用 SimpleEvaluationContext
// 限制可访问的功能,提高安全性
SimpleEvaluationContext simpleContext = SimpleEvaluationContext.forReadOnlyDataBinding().build();
parser.parseExpression("name").getValue(simpleContext, user);
避免代码注入
// 不要直接使用用户输入构建表达式
String userInput = "T(java.lang.Runtime).getRuntime().exec('rm -rf /')";
// 这种表达式是危险的!
性能优化技巧
-
缓存 Expression 对象:重复使用的表达式应该缓存
-
重用 EvaluationContext:避免频繁创建上下文
-
使用简单表达式:复杂的表达式会影响性能
// 缓存表达式
private static final Expression CACHED_EXPRESSION =
parser.parseExpression("name.toUpperCase()");
public String processUser(User user) {
return CACHED_EXPRESSION.getValue(context, user, String.class);
}
共有 0 条评论