当前位置: 首页 > article >正文

Spring Expression Language (SpEL)(详解)

SpEL详解

    • 一.简介
    • 二.SpEL 的操作符
      • 1. 集合操作符
        • 1.1 [ ](索引操作符)
        • 1.2 .?(选择操作符)
        • 1.3 .![ ](映射操作符)
        • 1.4 #[ ](限制操作符)
      • 2. 算术运算符
        • SpEL 支持基本的算术运算符,可以用于数字运算:
      • 3. 比较运算符
        • SpEL 支持常见的比较运算符:
      • 4. 逻辑运算符
        • SpEL 支持常见的逻辑运算符,用于布尔值的操作:
      • 5. 条件运算符
        • ?:(三元运算符)
      • 6. 类型操作符
        • 1. T()(类型引用操作符)
        • 2. instanceof(类型判断操作符)
      • 7. . 对象操作符
        • .(成员访问符)
      • 8. 空值安全操作符(?.)
      • 9. 集合操作符总结
    • 二.SpEL 的基本功能
      • 1. 变量解析
      • 2.字符串和算术运算
      • 3. 方法调用
      • 4. 对象属性访问
      • 5. 逻辑判断
      • 6. 集合操作
    • 三. SpEL 在 Spring 中的应用
      • 1.用于 Spring 配置中的条件判断
      • 2.用于 @PreAuthorize 中的权限控制
      • 3. 用于 @Value 注解中的动态值
      • 4. 用于 Spring EL 中的条件赋值
    • 四. SpEL 的高级功能
      • 1. 类型转换
      • 2. 复杂的集合操作
      • 3. 使用自定义方法
      • 4. 内嵌函数
    • 五. SpEL 的优势劣势
      • 1.优势
        • 1.灵活性和动态性
        • 2.简化代码
        • 3.支持条件和复杂表达式
        • 4.与 Spring 集成良好
        • 5.动态注入
        • 6.增强表达能力
      • 2.劣势
        • 1.性能开销
        • 2.调试困难
        • 3.代码可读性差
        • 4.过度依赖 Spring
        • 5.潜在的安全风险
        • 6.复杂的错误信息
      • 3.总结:何时使用 SpEL
        • 1.适合使用 SpEL的场景:
        • 2.不推荐过度使用 SpEL的场景:

一.简介

SpEL(Spring Expression Language)是一个强大的表达式语言,可以用于在 Spring 应用中进行动态计算和处理。它能够在 Spring 配置中、注解中、以及任何需要解析动态表达式的地方执行各种操作。SpEL 语法灵活,支持变量解析、方法调用、对象操作等功能,非常适用于动态配置、权限控制和条件判断等场景。
SpEL 表达式使用 #{} 来包围,它可以包含常量、变量、方法调用、运算符等。

二.SpEL 的操作符

1. 集合操作符

1.1 [ ](索引操作符)

用于访问集合、数组或列表中的元素
语法: collection[index]
例子:

@Value("#{myList[0]}")
private User firstUser;  // 获取 myList 中的第一个元素
1.2 .?(选择操作符)

用于对集合进行过滤和选择,通常结合条件来使用
语法: collection.?[condition]
例子:

@Value("#{myList.?[age > 18]}")
private List<User> adults;  // 筛选出年龄大于18的用户

1.3 .![ ](映射操作符)

用于对集合中的每个元素应用某种转换或操作,类似于映射(map)操作。
语法: collection.![expression]
例子:

@Value("#{myList.![name]}")
private List<String> names;  // 获取 myList 中所有用户的名字

1.4 #[ ](限制操作符)

用于选择集合中的一部分元素,类似于 limit 或 slice 操作。
语法: collection.#[(start, end)]
例子:

@Value("#{myList.#[(0, 2)]}")
private List<User> limitedList;  // 获取 myList 中索引从 0 到 2 的元素

2. 算术运算符

SpEL 支持基本的算术运算符,可以用于数字运算:

加法:+
减法:-
乘法:

除法:/
取余:%

例子:

@Value("#{2 + 3}")
private int sum;  // 结果是 5

@Value("#{10 - 4}")
private int difference;  // 结果是 6

@Value("#{5 * 6}")
private int product;  // 结果是 30

@Value("#{10 / 2}")
private int quotient;  // 结果是 5

@Value("#{10 % 3}")
private int remainder;  // 结果是 1

3. 比较运算符

SpEL 支持常见的比较运算符:

等于:==
不等于:!=
大于:>
小于:<
大于等于:>=
小于等于:<=

例子:

@Value("#{2 == 2}")
private boolean isEqual;  // 结果是 true

@Value("#{3 > 2}")
private boolean isGreaterThan;  // 结果是 true

@Value("#{5 <= 10}")
private boolean isLessThanOrEqual;  // 结果是 true

4. 逻辑运算符

SpEL 支持常见的逻辑运算符,用于布尔值的操作:

与:and
或:or
非:not

例子:

@Value("#{true and false}")
private boolean andResult;  // 结果是 false

@Value("#{true or false}")
private boolean orResult;  // 结果是 true

@Value("#{not true}")
private boolean notResult;  // 结果是 false

5. 条件运算符

?:(三元运算符)

条件运算符,类似于 Java 中的三元运算符 condition ? trueResult : falseResult。它用于在 SpEL 表达式中根据某个条件决定返回的值。

语法: condition ? trueValue : falseValue
例子:

@Value("#{age > 18 ? 'Adult' : 'Minor'}")
private String status;  // 如果 age > 18,返回 'Adult',否则返回 'Minor'

6. 类型操作符

1. T()(类型引用操作符)

用于引用 Java 类并访问其静态成员(方法、字段、常量)。
语法: T(类名).静态成员
例子:

@Value("#{T(java.lang.Math).PI}")
private double pi;  // 获取 Math.PI 常量值

@Value("#{T(java.lang.System).currentTimeMillis()}")
private long currentTime;  // 获取当前时间戳

2. instanceof(类型判断操作符)

用于判断对象是否是某个类的实例。
语法: object instanceof Class
例子:

@Value("#{myUser instanceof T(com.example.User)}")
private boolean isUser;  // 判断 myUser 是否是 User 类的实例

7. . 对象操作符

.(成员访问符)

用于访问对象的属性或调用对象的方法。
语法: object.property 或 object.method()
例子:

@Value("#{myUser.name}")
private String userName;  // 获取 myUser 对象的 name 属性值

@Value("#{myUser.getAge()}")
private int userAge;  // 调用 myUser 对象的 getAge() 方法

8. 空值安全操作符(?.)

类似于 Java 8 中的 Optional,SpEL 也支持空值安全的操作符 ?.,用于避免空指针异常。它允许你在对象为 null 时,安全地访问其属性或方法。

语法: object?.property 或 object?.method()
例子:

@Value("#{myUser?.name}")
private String userName;  // 如果 myUser 为 null,不会抛出异常,而是返回 null

9. 集合操作符总结

[ ]: 按索引访问集合中的元素。
.[]: 选择集合中符合条件的元素。
.?!: 按条件过滤集合元素(类似于 [])。
.![ ]: 映射集合中的每个元素。
.#[ ]: 选择集合的一个子集(类似于 slice 操作)。
.first(), .last():返回集合中的第一个或最后一个元素。

二.SpEL 的基本功能

1. 变量解析

SpEL 允许在表达式中引用变量,支持从上下文中提取数据。
例子:

@Value("#{systemProperties['user.name']}")
private String userName;

表达式:

#{myBean.myProperty}

其中,myBean 是 Spring 上下文中的一个 bean,myProperty 是该 bean 的属性。
注 systemProperties 是 Spring 内置的一种变量,代表 系统属性。
user.name 是一个预定义的系统属性,通常在操作系统中表示当前用户的用户名。

2.字符串和算术运算

SpEL 支持常见的字符串处理和算术运算。
例子:

@Value("#{2 * 3}")
private int result;  // result = 6

@Value("#{ 'Hello' + ' ' + 'World!' }")
private String greeting;  // greeting = "Hello World!"

3. 方法调用

SpEL 可以调用方法,包括静态方法和实例方法。
例子:

@Value("#{T(java.lang.Math).random()}")
private double randomValue;  // 调用静态方法生成随机数

@Value("#{myBean.calculateTotal(10, 20)}")
private int totalAmount;  // 调用实例方法

4. 对象属性访问

SpEL 支持对对象的属性、集合、数组的访问。
例子:

@Value("#{user.name}")
private String userName;  // 获取对象属性

@Value("#{user.address.city}")
private String cityName;  // 获取对象嵌套属性

5. 逻辑判断

SpEL 支持常见的逻辑运算符,例如 and, or, not 等
例子:

@Value("#{user.age > 18 ? 'Adult' : 'Child'}")
private String status;  // 根据 age 判断,年龄大于18为 Adult,否则为 Child

6. 集合操作

SpEL 支持对集合(List, Set, Map 等)进行操作,例如过滤、排序等。
例子:

@Value("#{myList[0]}")
private String firstItem;  // 获取列表的第一个元素

@Value("#{myMap['key']}")
private String mapValue;  // 获取 Map 中对应键的值

三. SpEL 在 Spring 中的应用

1.用于 Spring 配置中的条件判断

在 Spring 的配置文件中,可以使用 SpEL 来动态判断条件,从而加载不同的配置。
例子:

<bean id="exampleBean" class="com.example.MyBean" 
      p:property1="#{systemProperties['os.name'].contains('Windows') ? 'Windows Specific Value' : 'Other OS Value'}" />

2.用于 @PreAuthorize 中的权限控制

SpEL 在 Spring Security 中用于动态权限控制,能够在运行时解析和计算用户权限。
例子:

@PreAuthorize("hasAuthority('ROLE_ADMIN') and #user.username == authentication.name")
public String editUserProfile(User user) {
    return "User Profile Edited";
}


这里的 SpEL 表达式表示:用户必须有 ROLE_ADMIN 权限。
当前请求用户的用户名必须与传入的 user.username 匹配

3. 用于 @Value 注解中的动态值

spEL 在 @Value 注解中非常常见,允许你动态注入基于表达式的值
例子:

@Value("#{systemProperties['user.home']}")
private String userHome;  // 获取当前用户的 home 目录

@Value("#{T(java.lang.Math).PI}")
private double pi;  // 获取常量 PI

4. 用于 Spring EL 中的条件赋值

SpEL 可以用来为 Spring 配置中的属性赋值,包括动态生成值。
例子:

<bean id="exampleBean" class="com.example.MyBean">
    <property name="environment" value="#{systemProperties['os.name'] == 'Windows 10' ? 'Windows' : 'Other'}"/>
</bean>

四. SpEL 的高级功能

1. 类型转换

SpEL 支持类型转换,可以将对象或表达式的结果自动转换为目标类型。
例子:

@Value("#{T(java.lang.Integer).parseInt('123')}")
private int parsedInt;  // 解析字符串 '123' 为整数

2. 复杂的集合操作

SpEL 支持对集合进行复杂的操作,如过滤、排序、聚合等。

例子:

@Value("#{myList.?[age > 18]}")
private List<User> adultUsers;  // 获取列表中所有年龄大于18的用户

3. 使用自定义方法

可以使用 SpEL 调用自定义的静态方法或实例方法
例子:

@Value("#{T(com.example.MyUtils).calculateTax(price)}")
private double tax;  // 调用自定义工具类的静态方法计算税费

4. 内嵌函数

SpEL 还支持内嵌函数,如日期格式化、字符串操作等。
例子:

@Value("#{T(java.time.LocalDate).now().plusDays(5)}")
private LocalDate dueDate;  // 获取当前日期加上5天

五. SpEL 的优势劣势

1.优势

1.灵活性和动态性

SpEL 允许你在运行时动态计算和执行表达式,使得代码更加灵活。
例如,你可以在配置文件中动态注入值,或根据某些条件在运行时决定计算结果。
支持常见的动态特性,如字符串拼接、数学运算、条件判断等。

2.简化代码

SpEL 允许你通过注解来直接在 Spring 配置中表达复杂的逻辑,减少了样板代码的书写。
比如,通过 @Value 注解直接注入属性,避免了手动解析和赋值的过程。

3.支持条件和复杂表达式

SpEL 支持丰富的表达式,可以用来实现复杂的条件判断、集合操作、数学计算等,使得配置和代码更具表达性。
你可以使用三元运算符、逻辑运算符、集合操作符等灵活组合表达式。

4.与 Spring 集成良好

SpEL 是 Spring 框架的一部分,天然与 Spring 配置、数据绑定、AOP、事务等功能集成,能够充分发挥 Spring 框架的优势。
支持动态注入、Bean 属性的赋值、条件配置等。

5.动态注入

可以根据上下文中的值动态调整注入的内容,无需硬编码。这使得配置更加灵活,特别是在多环境配置或需要动态调整值时。

6.增强表达能力

SpEL 提供了强大的表达能力,允许在 Spring Bean 中执行 Java 表达式、访问静态成员、调用方法等。这增强了配置的可表达性。

2.劣势

1.性能开销

虽然 SpEL 提供了灵活性,但它会带来一定的性能开销。每次执行 SpEL 表达式时,Spring 都需要解析表达式并执行计算,尤其在复杂的表达式或频繁调用时,可能导致性能下降。
特别是在处理大量数据或高频调用时,性能问题可能变得明显。

2.调试困难

SpEL 表达式通常是字符串形式,无法在编译时进行类型检查和错误捕获。对于复杂的表达式,错误往往只有在运行时才能发现,这使得调试变得更加困难。
如果表达式有语法错误或者类型不匹配,Spring 容器会在启动时抛出异常,这可能不容易发现。

3.代码可读性差

由于 SpEL 表达式通常是嵌入在代码中的字符串,长而复杂的表达式可能会让代码变得不易理解。过度使用 SpEL 会导致代码的可维护性降低,特别是当表达式的逻辑较复杂时,其他开发人员很难快速理解其作用。
在某些场景下,SpEL 使得代码不如显式的方法调用直观,增加了理解的难度。

4.过度依赖 Spring

SpEL 是 Spring 的一部分,如果你在项目中大量依赖 SpEL,你的代码将与 Spring 强耦合,这使得代码难以迁移到非 Spring 环境或者替换掉 Spring 框架。
过度使用 SpEL 可能导致应用程序变得对 Spring 框架过于依赖,影响可移植性和扩展性。

5.潜在的安全风险

SpEL 是一种强大的表达式语言,它允许执行任意的 Java 方法。如果不谨慎使用,可能会带来安全隐患。例如,恶意的 SpEL 表达式可能执行不安全的操作,导致注入攻击等问题。
在使用 SpEL 时,应该小心防止用户输入的内容被直接当作表达式执行,尤其是当 SpEL 用于从外部数据源(如 HTTP 请求、数据库等)动态构造时。

6.复杂的错误信息

当 SpEL 表达式出错时,错误信息可能比较模糊,难以快速定位问题。特别是当表达式非常复杂时,错误信息可能会不够清晰,导致排查困难

3.总结:何时使用 SpEL

1.适合使用 SpEL的场景:

配置动态的、灵活的属性值(如:环境变量、系统属性)。
简化和动态注入 Spring Bean 中的值,减少样板代码。
在 Spring 配置中执行简单的运算和表达式,例如通过 @Value 注解注入计算结果。
需要根据条件动态配置某些值,或者根据集合数据进行操作时(如过滤、映射)。

2.不推荐过度使用 SpEL的场景:

性能要求较高的场景(例如大规模数据处理)。
需要高可读性和易于调试的场景,特别是团队合作时,过度使用 SpEL 会让代码难以理解。
安全敏感的场景,需要防止代码被恶意修改或执行不安全的操作。
总体来说,SpEL 提供了极大的灵活性和强大的表达能力,尤其在 Spring 项目中非常有用,但过度依赖或者使用不当可能会带来一些维护和性能上的问题。因此,建议在保证代码清晰、可维护的前提下合理使用 SpEL。


http://www.kler.cn/a/572529.html

相关文章:

  • 【每日学点HarmonyOS Next知识】tabs切换卡顿、输入框焦点、打开全新web、输入框密码类型、非法变量值
  • 当电脑JDK的位置被移动,如何修改IDEA中JDK被修改后的位置
  • 深入MiniQMT:实现远程下单的高效解决方案
  • 如何设计高并发分布式系统的唯一ID?主流方案深度解析与实战选型指南
  • RabbitMQ 2025/3/5
  • 优优绿能闯上市:业绩变脸,万帮新能源多次减持,实控人忙套现
  • 3dsmax中使用python创建PBR材质并挂接贴图
  • 6、什么是重排重绘?
  • Nginx 部署 Vue.js 项目指南:结合慈云数据服务器的实践
  • Vue Table 表格列筛选,前端筛选与后端筛选的写法
  • 4 Redis4 List命令类型讲解
  • C# IEquatable<T> 使用详解
  • Serilog: 强大的 .NET 日志库
  • c++中什么时候应该使用extern关键字?
  • 大模型管理工具:LLaMA-Factory
  • ssm_mysql_小型企业人事管理系统
  • c++进阶--继承
  • 【数据结构-图】
  • PostgreSQL 创建表格
  • 3D Web轻量化引擎HOOPS Communicator的核心优势解析:高性能可视化与灵活部署!