本文目录导读:

基础案例
基础表达式
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class SpELBasicCase {
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
// 字符串表达式
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();
System.out.println(message); // Hello World
// 数学运算
Expression exp2 = parser.parseExpression("10 + 5 * 2");
int result = (int) exp2.getValue();
System.out.println(result); // 20
// 字符串拼接
Expression exp3 = parser.parseExpression("'Hello' + ' ' + 'World'");
System.out.println(exp3.getValue()); // Hello World
}
}
使用 EvaluationContext
public class SpELContextCase {
@Data
static class User {
private String name;
private int age;
private List<String> hobbies;
private Address address;
}
@Data
static class Address {
private String city;
private String street;
}
public static void main(String[] args) {
// 创建用户对象
User user = new User();
user.setName("张三");
user.setAge(25);
user.setHobbies(Arrays.asList("编程", "阅读", "运动"));
Address address = new Address();
address.setCity("北京");
address.setStreet("长安街");
user.setAddress(address);
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("user", user);
// 访问对象属性
Expression exp1 = parser.parseExpression("name");
System.out.println(exp1.getValue(user)); // 张三
// 嵌套属性
Expression exp2 = parser.parseExpression("address.city");
System.out.println(exp2.getValue(user)); // 北京
// 方法调用
Expression exp3 = parser.parseExpression("name.length()");
System.out.println(exp3.getValue(user)); // 2
// 集合访问
Expression exp4 = parser.parseExpression("hobbies[0]");
System.out.println(exp4.getValue(user)); // 编程
}
}
集合操作案例
列表和映射操作
public class SpELCollectionCase {
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
// 创建集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 25);
context.setVariable("numbers", numbers);
context.setVariable("map", map);
// 列表推导(过滤)
Expression exp1 = parser.parseExpression("#numbers.?[#this > 3]");
List<Integer> filtered = (List<Integer>) exp1.getValue(context);
System.out.println(filtered); // [4, 5]
// 列表推导(映射)
Expression exp2 = parser.parseExpression("#numbers.![#this * 2]");
List<Integer> mapped = (List<Integer>) exp2.getValue(context);
System.out.println(mapped); // [2, 4, 6, 8, 10]
// 筛选第一个匹配项
Expression exp3 = parser.parseExpression("#numbers.^[#this > 2]");
Integer firstMatch = (Integer) exp3.getValue(context);
System.out.println(firstMatch); // 3
// 筛选最后一个匹配项
Expression exp4 = parser.parseExpression("#numbers.$[#this < 4]");
Integer lastMatch = (Integer) exp4.getValue(context);
System.out.println(lastMatch); // 3
// 集合判断
Expression exp5 = parser.parseExpression("#numbers.?[#this > 2].size() > 2");
Boolean result = exp5.getValue(context, Boolean.class);
System.out.println(result); // true
}
}
对象列表操作
public class SpELListObjectCase {
@Data
@AllArgsConstructor
static class Product {
private String name;
private Double price;
private Integer stock;
private String category;
}
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
List<Product> products = Arrays.asList(
new Product("iPhone", 5999.0, 100, "手机"),
new Product("华为", 3999.0, 200, "手机"),
new Product("MacBook", 12999.0, 50, "电脑"),
new Product("iPad", 3499.0, 150, "平板")
);
context.setVariable("products", products);
// 筛选价格大于4000的产品
Expression exp1 = parser.parseExpression("#products.?[price > 4000]");
List<Product> expensiveProducts = (List<Product>) exp1.getValue(context);
System.out.println("高价产品: " + expensiveProducts);
// 筛选特定类别并按价格排序
Expression exp2 = parser.parseExpression("#products.?[category == '手机'].![name]");
List<String> phoneNames = (List<String>) exp2.getValue(context);
System.out.println("手机品牌: " + phoneNames);
// 统计平均值
Expression exp3 = parser.parseExpression("#products.![price].^[true]");
List<Double> prices = (List<Double>) exp3.getValue(context);
double average = prices.stream().mapToDouble(Double::doubleValue).average().orElse(0);
System.out.println("平均价格: " + average);
// 条件判断所有产品
Expression exp4 = parser.parseExpression("#products.![stock > 0].^[true]");
System.out.println("所有产品有库存: " + exp4.getValue(context));
}
}
条件逻辑案例
条件表达式
public class SpELConditionCase {
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
// 三元运算符
Expression exp1 = parser.parseExpression("10 > 5 ? '正确' : '错误'");
String result1 = (String) exp1.getValue();
System.out.println(result1); // 正确
// Elvis运算符(类似三元)
Expression exp2 = parser.parseExpression("name != null ? name : '默认值'");
// 等价于
Expression exp3 = parser.parseExpression("name ?: '默认值'");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("name", null);
System.out.println(exp3.getValue(context)); // 默认值
// 安全导航操作符(避免空指针)
context.setVariable("user", null);
Expression exp4 = parser.parseExpression("user?.name?.toUpperCase()");
Object result = exp4.getValue(context);
System.out.println("安全导航结果: " + result); // null(不会抛异常)
// 复杂的逻辑判断
Expression exp5 = parser.parseExpression(
"(age > 18 && age < 65) || (age >= 65 && isRetired)"
);
StandardEvaluationContext userContext = new StandardEvaluationContext();
userContext.setVariable("age", 70);
userContext.setVariable("isRetired", true);
System.out.println(exp5.getValue(userContext)); // true
}
}
业务规则案例
public class SpELBusinessRuleCase {
@Data
@AllArgsConstructor
static class Order {
private double amount;
private Customer customer;
private List<OrderItem> items;
}
@Data
@AllArgsConstructor
static class Customer {
private String level; // VIP / NORMAL
private int loyaltyYears;
}
@Data
@AllArgsConstructor
static class OrderItem {
private String productName;
private double price;
private int quantity;
}
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
// 创建订单
Customer vipCustomer = new Customer("VIP", 5);
List<OrderItem> items = Arrays.asList(
new OrderItem("电脑", 8000, 1),
new OrderItem("鼠标", 100, 2)
);
Order order = new Order(8200, vipCustomer, items);
context.setVariable("order", order);
// 规则1:VIP客户且忠诚度超过3年,享受95折
String vipRule =
"#order.customer.level == 'VIP' and #order.customer.loyaltyYears > 3";
boolean isVipEligible = parser.parseExpression(vipRule)
.getValue(context, Boolean.class);
System.out.println("VIP折扣资格: " + isVipEligible);
// 规则2:订单金额超过5000,可以免运费
String freeShippingRule = "#order.amount > 5000";
boolean freeShipping = parser.parseExpression(freeShippingRule)
.getValue(context, Boolean.class);
System.out.println("免运费资格: " + freeShipping);
// 规则3:包含特定商品
String hasComputerRule =
"#order.items.?[productName == '电脑'].size() > 0";
boolean hasComputer = parser.parseExpression(hasComputerRule)
.getValue(context, Boolean.class);
System.out.println("包含电脑: " + hasComputer);
// 规则4:计算折扣金额
String discountRule =
"#order.amount * (#order.customer.level == 'VIP' ? 0.95 : 1.0)";
double finalAmount = parser.parseExpression(discountRule)
.getValue(context, Double.class);
System.out.println("最终金额: " + finalAmount);
}
}
Spring 注解使用案例
@Value 注解
@Component
public class SpELValueAnnotationCase {
// 字符串字面量
@Value("'Hello World'")
private String message;
// 系统属性
@Value("#{systemProperties['user.home']}")
private String userHome;
// 引用其他Bean属性
@Value("#{databaseConfig.url}")
private String databaseUrl;
// 调用方法
@Value("#{propertiesUtil.getProperty('app.name', '默认应用名')}")
private String appName;
// 静态常量
@Value("#{T(java.lang.Math).PI}")
private double pi;
// 复杂表达式
@Value("#{systemProperties['user.region'] == 'CN' ? '中国大陆' : '其他地区'}")
private String region;
// 列表
@Value("#{{'spring', 'mybatis', 'redis'}}")
private List<String> frameworks;
// Map
@Value("#{{'key1': 'value1', 'key2': 'value2'}}")
private Map<String, String> configMap;
// 数组
@Value("#{new String[]{'item1', 'item2', 'item3'}}")
private String[] items;
// 引用配置文件中的值
@Value("${app.name:默认名称}")
private String appNameFromConfig;
public void showConfigs() {
System.out.println("Message: " + message);
System.out.println("User Home: " + userHome);
System.out.println("Database URL: " + databaseUrl);
System.out.println("App Name: " + appName);
System.out.println("PI: " + pi);
System.out.println("Region: " + region);
System.out.println("Frameworks: " + frameworks);
System.out.println("Config Map: " + configMap);
System.out.println("Items: " + Arrays.toString(items));
}
}
@PreAuthorize 注解(Spring Security)
@RestController
public class SpELSecurityCase {
// 基于角色的权限控制
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/data")
public String adminData() {
return "Admin Data";
}
// 基于用户名
@PreAuthorize("#username == authentication.principal.username")
@GetMapping("/user/{username}")
public String userData(@PathVariable String username) {
return "User: " + username;
}
// 自定义方法
@PreAuthorize("@securityService.checkPermission(authentication, #request)")
@PostMapping("/api/secure")
public String secureEndpoint(@RequestBody ApiRequest request,
Authentication authentication) {
return "Secure response";
}
// 复杂的权限表达式
@PreAuthorize("hasAnyRole('ADMIN', 'MANAGER') AND " +
"(hasPermission(#id, 'READ') OR #userId == authentication.principal.id)")
@GetMapping("/document/{id}")
public String getDocument(@PathVariable Long id, @RequestParam Long userId) {
return "Document: " + id;
}
}
高级案例
自定义函数
public class SpELCustomFunctionCase {
public static String formatDate(LocalDate date) {
return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
public static double calculateDiscount(double price, double discountRate) {
return price * (1 - discountRate);
}
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
// 注册静态方法
context.registerFunction("formatDate",
SpELCustomFunctionCase.class.getDeclaredMethod("formatDate", LocalDate.class));
context.registerFunction("calculateDiscount",
SpELCustomFunctionCase.class.getDeclaredMethod("calculateDiscount", double.class, double.class));
// 使用自定义函数
Expression exp1 = parser.parseExpression("#formatDate(T(java.time.LocalDate).now())");
String dateStr = (String) exp1.getValue(context);
System.out.println("格式化日期: " + dateStr);
// 计算折扣
Expression exp2 = parser.parseExpression("#calculateDiscount(1000, 0.15)");
double price = (double) exp2.getValue(context);
System.out.println("折扣价格: " + price);
}
}
动态规则引擎案例
public class SpringELRuleEngine {
static class Rule {
private String name;
private String condition;
private String action;
private int priority;
}
@Data
@AllArgsConstructor
static class Transaction {
private double amount;
private String type; // TRANSFER / PURCHASE / WITHDRAW
private String from;
private String to;
}
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
// 定义规则
List<Rule> rules = Arrays.asList(
new Rule("大额转账",
"#trans.amount > 10000 and #trans.type == 'TRANSFER'",
"执行大额转账审核流程", 1),
new Rule("频繁交易",
"#trans.amount > 5000 and #trans.type == 'PURCHASE'",
"标记为高消费用户", 2),
new Rule("异常取现",
"#trans.type == 'WITHDRAW' and #trans.amount > 50000",
"触发取现风控", 1)
);
// 测试事务
Transaction t1 = new Transaction(15000, "TRANSFER", "A001", "B002");
Transaction t2 = new Transaction(8000, "PURCHASE", "A001", "SHOP001");
Transaction t3 = new Transaction(60000, "WITHDRAW", "A001", "CASH");
for (Transaction trans : Arrays.asList(t1, t2, t3)) {
System.out.println("\n=== 事务检查: " + trans.getType() + " " + trans.getAmount() + " ===");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("trans", trans);
for (Rule rule : rules) {
boolean matched = parser.parseExpression(rule.condition)
.getValue(context, Boolean.class);
if (matched) {
System.out.println("触发规则: " + rule.name + " -> " + rule.action);
}
}
}
}
}
性能优化案例
public class SpELPerformanceCase {
static class ExpressionCache {
private static final ConcurrentHashMap<String, Expression> CACHE = new ConcurrentHashMap<>();
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
public static Expression getExpression(String expr) {
return CACHE.computeIfAbsent(expr, PARSER::parseExpression);
}
}
public static void main(String[] args) {
// 编译模式的性能优化
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,
ExpressionCache.class.getClassLoader()
);
SpelExpressionParser compilerParser = new SpelExpressionParser(config);
// 使用编译模式
String expression = "name.toUpperCase() + ' - ' + age";
StandardEvaluationContext context = new StandardEvaluationContext();
User user = new User();
user.setName("张三");
user.setAge(25);
// 缓存表达式避免重复解析
Expression exp = ExpressionCache.getExpression(expression);
// 性能测试
long start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
Object result = exp.getValue(user);
}
long end = System.nanoTime();
System.out.println("执行100000次耗时: " + (end - start) / 1_000_000 + "ms");
}
}
这些案例展示了 SpEL 在不同场景下的应用:
- 基础表达式:字面量、运算符、方法调用
- 集合操作:过滤、映射、投影、聚合
- 条件逻辑:三元运算符、安全导航
- Spring 注解:@Value、@PreAuthorize
- 自定义函数:注册和使用自定义方法
- 规则引擎:动态业务规则处理
- 性能优化:编译模式、表达式缓存
SpEL 非常灵活,可以处理复杂的表达式,但要注意:
- 复杂表达式可能影响性能
- 避免在循环中重复创建表达式
- 适当使用缓存提高性能
- 注意表达式安全性(避免注入攻击)