SpEL表达式(Spring Expression Language)详解
SpEL(Spring Expression Language)是Spring框架提供的一种强大的表达式语言,用于在运行时查询和操作对象图,支持字面量、运算符、方法调用、属性访问、正则匹配、集合操作等,可独立使用或集成到Spring框架(如注解、XML配置、Bean定义)中。
一、核心特点
- 动态求值:运行时解析表达式,而非编译期;
- 跨场景使用:支持XML配置、注解(如
@Value)、编程式调用; - 丰富的语法:涵盖字面量、运算符、类型转换、集合操作、Bean引用等;
- 与Spring无缝集成:可直接访问Spring容器中的Bean、环境变量、系统属性等。
二、基本语法与常用场景
1. 字面量表达式
支持字符串、数字、布尔、null等基础类型的直接表达:
// 编程式调用示例(核心类:ExpressionParser、SpelExpressionParser、EvaluationContext)ExpressionParserparser=newSpelExpressionParser();// 字符串(单引号包裹)Expressionexp1=parser.parseExpression("'Hello SpEL'");Stringstr=(String)exp1.getValue();// 结果:Hello SpEL// 数字Expressionexp2=parser.parseExpression("100 + 200 * 2");intnum=(Integer)exp2.getValue();// 结果:500// 布尔Expressionexp3=parser.parseExpression("true && false");booleanbool=(Boolean)exp3.getValue();// 结果:false// nullExpressionexp4=parser.parseExpression("null");ObjectnullObj=exp4.getValue();// 结果:null2. 属性与方法调用
通过.访问对象属性/方法,支持嵌套访问:
// 定义测试对象classUser{privateStringname;privateList<String>hobbies;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicList<String>getHobbies(){returnhobbies;}publicvoidsetHobbies(List<String>hobbies){this.hobbies=hobbies;}publicStringsayHello(Stringmsg){return"Hello: "+msg;}}// 初始化上下文Useruser=newUser();user.setName("张三");user.setHobbies(Arrays.asList("篮球","读书"));EvaluationContextcontext=newStandardEvaluationContext(user);// 访问属性Stringname=(String)parser.parseExpression("name").getValue(context);// 张三// 调用方法(带参数)Stringhello=(String)parser.parseExpression("sayHello('SpEL')").getValue(context);// Hello: SpEL// 嵌套访问(集合属性)Stringhobby=(String)parser.parseExpression("hobbies[0]").getValue(context);// 篮球3. 运算符
支持算术、逻辑、关系、赋值、三元运算符等:
| 类型 | 运算符示例 | 说明 |
|---|---|---|
| 算术 | 10 + 2、3 * 4、10 % 3 | 加减乘除、取模 |
| 逻辑 | `true | |
| 关系 | 5 > 3、'abc' == 'abc' | 大于、等于、不等于等 |
| 赋值 | name = '李四' | 为属性赋值 |
| 三元运算 | name == '张三' ? '是' : '否' | 条件判断 |
| Elvis运算符 | name ?: '默认值' | 简化空判断(name为空则返回默认值) |
示例:
// 三元运算Stringresult=(String)parser.parseExpression("name == '张三' ? '管理员' : '普通用户'").getValue(context);// 管理员// Elvis运算符(空保护)parser.parseExpression("name = null").getValue(context);StringdefaultName=(String)parser.parseExpression("name ?: '未知用户'").getValue(context);// 未知用户4. 集合操作
支持访问集合元素、筛选、投影等:
// 1. 访问List/MapList<Integer>nums=Arrays.asList(1,2,3,4);EvaluationContextlistContext=newStandardEvaluationContext(nums);intfirst=(Integer)parser.parseExpression("[0]").getValue(listContext);// 1Map<String,Integer>map=newHashMap<>();map.put("a",10);map.put("b",20);EvaluationContextmapContext=newStandardEvaluationContext(map);intval=(Integer)parser.parseExpression("['b']").getValue(mapContext);// 20// 2. 集合筛选(.?[表达式])List<Integer>evenNums=(List<Integer>)parser.parseExpression(".?[#this % 2 == 0]").getValue(listContext);// [2,4]// 3. 集合投影(.![表达式])List<String>strNums=(List<String>)parser.parseExpression(".![#this + '号']").getValue(listContext);// [1号,2号,3号,4号]5. Spring容器集成
(1)引用Spring Bean(@符号)
在Spring环境中,可直接引用容器中的Bean:
// 假设容器中有一个名为"userService"的Bean@Value("#{userService}")privateUserServiceuserService;// 调用Bean的方法@Value("#{userService.getUserName(1)}")privateStringuserName;(2)访问环境变量/系统属性
// 系统属性(systemProperties)@Value("#{systemProperties['os.name']}")privateStringosName;// 例如:Windows 10// 环境变量(environment)@Value("#{environment['JAVA_HOME']}")privateStringjavaHome;// Spring配置属性(application.properties)@Value("#{my.properties['app.name']}")privateStringappName;6. 注解中的常用场景
(1)@Value注解(区别:${}是占位符,#{}是SpEL)
// 基础值@Value("#{100 * 2}")privateintnum;// 200// 空判断@Value("#{user.name ?: '默认名称'}")privateStringuserName;// 集合@Value("#{T(java.util.Arrays).asList('a','b','c')}")privateList<String>letters;(2)@PreAuthorize(Spring Security权限控制)
// 权限表达式(判断用户是否有指定角色)@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")publicvoiddeleteUser(LonguserId){// 业务逻辑}三、编程式使用SpEL的完整示例
importorg.springframework.expression.Expression;importorg.springframework.expression.ExpressionParser;importorg.springframework.expression.spel.standard.SpelExpressionParser;importorg.springframework.expression.spel.support.StandardEvaluationContext;publicclassSpELDemo{publicstaticvoidmain(String[]args){// 1. 创建表达式解析器ExpressionParserparser=newSpelExpressionParser();// 2. 定义上下文(绑定对象)Useruser=newUser();user.setName("李四");user.setAge(25);StandardEvaluationContextcontext=newStandardEvaluationContext(user);// 3. 解析表达式// 访问属性Stringname=(String)parser.parseExpression("name").getValue(context);System.out.println("姓名:"+name);// 李四// 运算 + 赋值parser.parseExpression("age = age + 1").getValue(context);intage=(Integer)parser.parseExpression("age").getValue(context);System.out.println("年龄:"+age);// 26// 方法调用Stringgreet=(String)parser.parseExpression("sayHello('SpEL')").getValue(context);System.out.println(greet);// Hello: SpEL}staticclassUser{privateStringname;privateintage;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}publicStringsayHello(Stringmsg){return"Hello: "+msg;}}}四、注意事项
- 性能:SpEL运行时解析,高频调用场景需缓存解析后的
Expression对象; - 安全:避免解析用户输入的表达式(防止注入攻击);
- 区别
${}与#{}:${}:Spring占位符,仅做字符串替换(先解析);#{}:SpEL表达式,支持动态求值(后解析);- 混合使用:
@Value("${app.name:#{systemProperties['user.name']}}")(占位符默认值用SpEL);
- 类型转换:SpEL内置类型转换器,可自定义
TypeConverter扩展。
五、核心API总结
| 类/接口 | 作用 |
|---|---|
ExpressionParser | 表达式解析器(核心接口) |
SpelExpressionParser | ExpressionParser的默认实现 |
EvaluationContext | 表达式求值上下文(绑定变量、Bean等) |
StandardEvaluationContext | EvaluationContext的默认实现 |
Expression | 解析后的表达式对象(可多次求值) |
SpEL是Spring生态中灵活的动态表达式工具,核心场景包括配置注入、权限控制、动态规则判断等,掌握其语法可大幅提升Spring开发的灵活性。