Appearance
Spring Expression Language (SpEL) 操作符详解 🚀
前言:为什么需要 SpEL 操作符?
想象一下,如果我们在开发 Spring 应用时,需要在配置文件中进行复杂的条件判断、数学运算或字符串处理,传统的静态配置方式就显得力不从心了。SpEL(Spring Expression Language)操作符就像是给配置文件装上了"大脑",让它们能够进行动态计算和逻辑判断。
SpEL 操作符的核心价值
SpEL 操作符让我们能够在 Spring 配置中编写类似编程语言的表达式,实现动态配置、条件判断、数据转换等功能,大大提升了配置的灵活性和表达能力。
1. 关系操作符 (Relational Operators) 🔍
关系操作符用于比较两个值,返回布尔结果。它们是条件判断的基础。
1.1 基本关系操作符
SpEL 支持所有标准的关系操作符,并且为了避免在 XML 等文档中的符号冲突,还提供了文本等价形式:
符号 | 文本形式 | 含义 |
---|---|---|
== | eq | 等于 |
!= | ne | 不等于 |
< | lt | 小于 |
<= | le | 小于等于 |
> | gt | 大于 |
>= | ge | 大于等于 |
kotlin
@Service
class ProductService {
@Value("#{2 == 2}")
private val isEqual: Boolean = false // 结果: true
@Value("#{product.price gt 100}")
private val isExpensive: Boolean = false
@Value("#{'premium' eq product.category}")
private val isPremium: Boolean = false
fun checkProductStatus(product: Product): String {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext(product)
// 数字比较
val isAffordable = parser.parseExpression("price <= 50.0")
.getValue(context, Boolean::class.java)
// 字符串比较(按字典序)
val categoryCheck = parser.parseExpression("'electronics' < 'furniture'")
.getValue(Boolean::class.java) // 结果: true
return when {
isAffordable -> "经济实惠"
else -> "价格较高"
}
}
}
kotlin
data class CustomValue(val value: Int) : Comparable<CustomValue> {
override fun compareTo(other: CustomValue): Int {
return this.value.compareTo(other.value)
}
}
@Component
class ComparisonService {
fun compareCustomObjects(): Boolean {
val parser = SpelExpressionParser()
// 使用自定义对象的 compareTo 方法
return parser.parseExpression("new CustomValue(1) < new CustomValue(2)")
.getValue(Boolean::class.java) // 结果: true
}
}
1.2 特殊关系操作符
SpEL 还提供了三个特殊的关系操作符:
between 操作符
kotlin
@Service
class RangeCheckService {
fun checkValueInRange(value: Int, min: Int, max: Int): Boolean {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setVariable("val", value)
context.setVariable("min", min)
context.setVariable("max", max)
// between 操作符等价于 >= min && <= max
return parser.parseExpression("#val between {#min, #max}")
.getValue(context, Boolean::class.java)
}
@Value("#{student.score between {60, 100}}")
private val isPassing: Boolean = false
}
between 操作符注意事项
between
操作符的语法是 <input> between {<range_begin>, <range_end>}
,相当于 <input> >= <range_begin> && <input> <= <range_end>
。
因此 1 between {1, 5}
返回 true
,而 1 between {5, 1}
返回 false
。
instanceof 操作符
kotlin
@Component
class TypeCheckService {
fun checkTypes(): Map<String, Boolean> {
val parser = SpelExpressionParser()
return mapOf(
"integerCheck" to parser.parseExpression("123 instanceof T(Integer)")
.getValue(Boolean::class.java), // true
"stringCheck" to parser.parseExpression("'hello' instanceof T(String)")
.getValue(Boolean::class.java), // true
"wrongTypeCheck" to parser.parseExpression("'xyz' instanceof T(Integer)")
.getValue(Boolean::class.java) // false
)
}
}
基本类型装箱问题
注意基本类型会被自动装箱。1 instanceof T(int)
返回 false
,而 1 instanceof T(Integer)
返回 true
。
matches 操作符(正则表达式)
kotlin
@Service
class ValidationService {
fun validateInput(input: String): ValidationResult {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setVariable("input", input)
// 验证金额格式(支持小数点后两位)
val isValidAmount = parser.parseExpression(
"#input matches '^-?\\d+(\\.\\d{2})?$'"
).getValue(context, Boolean::class.java)
// 验证邮箱格式
val isValidEmail = parser.parseExpression(
"#input matches '^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$'"
).getValue(context, Boolean::class.java)
return ValidationResult(isValidAmount, isValidEmail)
}
}
data class ValidationResult(
val isValidAmount: Boolean,
val isValidEmail: Boolean
)
1.3 null 值比较规则
kotlin
@Component
class NullComparisonService {
fun demonstrateNullComparison(): Map<String, Boolean> {
val parser = SpelExpressionParser()
return mapOf(
// null 比较规则:任何值都大于 null
"greaterThanNull" to parser.parseExpression("5 > null")
.getValue(Boolean::class.java), // true
"lessThanNull" to parser.parseExpression("5 < null")
.getValue(Boolean::class.java), // false
// 推荐:使用与零的比较替代与 null 的比较
"saferComparison" to parser.parseExpression("score > 0")
.getValue(Boolean::class.java)
)
}
}
2. 逻辑操作符 (Logical Operators) 🧠
逻辑操作符用于组合布尔表达式,实现复杂的条件判断。
2.1 基本逻辑操作符
符号 | 文本形式 | 含义 |
---|---|---|
&& | and | 逻辑与 |
|| | or | 逻辑或 |
! | not | 逻辑非 |
kotlin
@Service
class UserPermissionService {
fun checkUserAccess(user: User): AccessResult {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext(user)
// 复合条件:用户必须是激活状态且有管理员权限
val hasAdminAccess = parser.parseExpression(
"isActive and hasRole('ADMIN')"
).getValue(context, Boolean::class.java)
// 复合条件:用户是VIP或者是付费用户
val hasPremiumAccess = parser.parseExpression(
"isVip or isPaidUser"
).getValue(context, Boolean::class.java)
// 排除条件:不是被封禁的用户
val isNotBanned = parser.parseExpression(
"!isBanned"
).getValue(context, Boolean::class.java)
return AccessResult(hasAdminAccess, hasPremiumAccess, isNotBanned)
}
}
data class AccessResult(
val hasAdminAccess: Boolean,
val hasPremiumAccess: Boolean,
val isNotBanned: Boolean
)
2.2 实际业务场景应用
kotlin
@Component
class BusinessRuleEngine {
@Value("#{user.age >= 18 and user.hasValidId}")
private val canVote: Boolean = false
@Value("#{order.amount > 100 or customer.isVip}")
private val qualifiesForDiscount: Boolean = false
fun evaluateComplexBusinessRule(context: BusinessContext): Boolean {
val parser = SpelExpressionParser()
val evalContext = StandardEvaluationContext(context)
// 复杂业务规则:
// (用户是会员 AND 订单金额>50) OR (用户是新用户 AND 首次购买)
val expression = """
(user.isMember and order.amount > 50) or
(user.isNewUser and order.isFirstPurchase)
""".trimIndent()
return parser.parseExpression(expression)
.getValue(evalContext, Boolean::class.java)
}
}
3. 字符串操作符 (String Operators) 📝
SpEL 为字符串提供了三种特殊操作符,让字符串处理更加灵活。
3.1 字符串连接 (+)
kotlin
@Service
class MessageService {
@Value("#{'Hello' + ' ' + 'World'}")
private val greeting: String = "" // 结果: "Hello World"
fun buildDynamicMessage(user: User): String {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext(user)
// 动态构建欢迎消息
return parser.parseExpression(
"'欢迎, ' + firstName + ' ' + lastName + '!'"
).getValue(context, String::class.java)
}
fun formatOrderInfo(order: Order): String {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext(order)
// 格式化订单信息
return parser.parseExpression(
"'订单号: ' + orderNumber + ', 金额: ¥' + amount"
).getValue(context, String::class.java)
}
}
3.2 字符减法 (-)
这是一个有趣的特性,可以对单个字符进行算术运算:
kotlin
@Component
class CharacterService {
fun demonstrateCharacterArithmetic(): Map<String, Char> {
val parser = SpelExpressionParser()
return mapOf(
// 字符 'd' 减去 3,得到 'a'
"charSubtraction" to parser.parseExpression("'d' - 3")
.getValue(Character::class.java), // 'a'
// 可以用于简单的字符编码转换
"nextChar" to parser.parseExpression("'A' + 1")
.getValue(Character::class.java), // 'B'
)
}
fun generateSequentialChars(start: Char, count: Int): List<Char> {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setVariable("start", start)
return (0 until count).map { i ->
context.setVariable("offset", i)
parser.parseExpression("#start + #offset")
.getValue(context, Character::class.java)
}
}
}
3.3 字符串重复 (*)
kotlin
@Service
class StringUtilService {
@Value("#{'*' * 10}")
private val separator: String = "" // 结果: "**********"
fun createProgressBar(progress: Int, total: Int = 20): String {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
val filled = (progress * total) / 100
val empty = total - filled
context.setVariable("filled", filled)
context.setVariable("empty", empty)
// 创建进度条:[████████░░] 80%
val progressBar = parser.parseExpression(
"'[' + ('█' * #filled) + ('░' * #empty) + ']'"
).getValue(context, String::class.java)
return "$progressBar $progress%"
}
fun createTableBorder(width: Int): String {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setVariable("width", width)
return parser.parseExpression("'-' * #width")
.getValue(context, String::class.java)
}
}
4. 数学操作符 (Mathematical Operators) 🔢
SpEL 支持完整的数学运算,包括基本运算和高级运算。
4.1 基本数学运算
kotlin
@Service
class CalculationService {
fun performBasicCalculations(): CalculationResults {
val parser = SpelExpressionParser()
return CalculationResults(
addition = parser.parseExpression("10 + 5")
.getValue(Int::class.java), // 15
subtraction = parser.parseExpression("10 - 3")
.getValue(Int::class.java), // 7
multiplication = parser.parseExpression("4 * 6")
.getValue(Int::class.java), // 24
division = parser.parseExpression("20 / 4")
.getValue(Int::class.java), // 5
modulus = parser.parseExpression("17 % 5")
.getValue(Int::class.java), // 2
power = parser.parseExpression("2^8")
.getValue(Int::class.java) // 256
)
}
}
data class CalculationResults(
val addition: Int,
val subtraction: Int,
val multiplication: Int,
val division: Int,
val modulus: Int,
val power: Int
)
4.2 增量和减量操作符
kotlin
@Component
class CounterService {
fun demonstrateIncrementDecrement(): Unit {
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
val counter = Counter(0)
// 后置递增:先使用值,再递增
val result1 = parser.parseExpression("counter++")
.getValue(context, counter, Int::class.java) // 返回 0,counter 变为 1
// 前置递增:先递增,再使用值
val result2 = parser.parseExpression("++counter")
.getValue(context, counter, Int::class.java) // 返回 2,counter 变为 2
// 后置递减:先使用值,再递减
val result3 = parser.parseExpression("counter--")
.getValue(context, counter, Int::class.java) // 返回 2,counter 变为 1
// 前置递减:先递减,再使用值
val result4 = parser.parseExpression("--counter")
.getValue(context, counter, Int::class.java) // 返回 0,counter 变为 0
println("Results: $result1, $result2, $result3, $result4")
println("Final counter value: ${counter.value}")
}
}
data class Counter(var value: Int) {
fun getCounter(): Int = value
fun setCounter(value: Int) { this.value = value }
}
4.3 运算符优先级
kotlin
@Service
class PrecedenceService {
fun demonstrateOperatorPrecedence(): Map<String, Int> {
val parser = SpelExpressionParser()
return mapOf(
// 乘法优先于加法和减法
"basicPrecedence" to parser.parseExpression("1 + 2 * 3")
.getValue(Int::class.java), // 7 (不是 9)
// 幂运算优先级最高
"powerPrecedence" to parser.parseExpression("2 + 3^2")
.getValue(Int::class.java), // 11 (不是 25)
// 使用括号改变优先级
"parentheses" to parser.parseExpression("(2 + 3) * 4")
.getValue(Int::class.java), // 20
// 复杂表达式
"complex" to parser.parseExpression("2^3 + 4 * 5 - 6 / 2")
.getValue(Int::class.java) // 8 + 20 - 3 = 25
)
}
}
4.4 文本等价形式
为了避免在 XML 中的符号冲突,除法和取模运算符也有文本形式:
kotlin
@Configuration
class MathConfiguration {
// 在 XML 配置中特别有用
@Value("#{100 div 3}") // 等价于 100 / 3
private val divisionResult: Int = 0
@Value("#{100 mod 7}") // 等价于 100 % 7
private val modulusResult: Int = 0
@Bean
fun mathService(): MathService {
return MathService(divisionResult, modulusResult)
}
}
5. 赋值操作符 (Assignment Operator) ✏️
赋值操作符用于动态设置对象的属性值。
5.1 基本赋值操作
kotlin
@Service
class PropertyService {
fun updateUserProperties(user: User): User {
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
// 方式1:使用 setValue 方法
parser.parseExpression("name")
.setValue(context, user, "张三")
parser.parseExpression("age")
.setValue(context, user, 25)
// 方式2:在 getValue 中使用赋值表达式
val newEmail = parser.parseExpression("email = '[email protected]'")
.getValue(context, user, String::class.java)
return user
}
}
5.2 动态配置更新
kotlin
@Component
class ConfigurationService {
fun updateConfiguration(config: AppConfig, updates: Map<String, Any>): AppConfig {
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
updates.forEach { (property, value) ->
try {
// 动态设置配置属性
parser.parseExpression(property)
.setValue(context, config, value)
println("Updated $property to $value")
} catch (e: Exception) {
println("Failed to update $property: ${e.message}")
}
}
return config
}
fun batchUpdateUsers(users: List<User>): List<User> {
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
return users.map { user ->
// 批量更新:将所有用户状态设为激活
parser.parseExpression("status = 'ACTIVE'")
.getValue(context, user, String::class.java)
user
}
}
}
6. 操作符重载 (Overloaded Operators) 🔧
SpEL 允许我们为自定义类型重载操作符,这是一个非常强大的特性。
6.1 实现自定义操作符重载
kotlin
// 自定义操作符重载器:支持列表连接
class ListConcatenationOverloader : OperatorOverloader {
override fun overridesOperation(
operation: Operation,
left: Any?,
right: Any?
): Boolean {
return operation == Operation.ADD &&
left is List<*> &&
right is List<*>
}
override fun operate(
operation: Operation,
left: Any?,
right: Any?
): Any {
if (operation == Operation.ADD &&
left is List<*> &&
right is List<*>) {
val result = ArrayList<Any?>(left)
result.addAll(right)
return result
}
throw UnsupportedOperationException(
"No overload for operation $operation and operands [$left] and [$right]"
)
}
}
6.2 使用自定义操作符
kotlin
@Service
class CustomOperatorService {
fun demonstrateListConcatenation(): List<Int> {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
// 注册自定义操作符重载器
context.operatorOverloader = ListConcatenationOverloader()
// 现在可以使用 + 操作符连接列表
return parser.parseExpression("{1, 2, 3} + {4, 5}")
.getValue(context, List::class.java) as List<Int> // [1, 2, 3, 4, 5]
}
fun createComplexListOperations(): List<Any> {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.operatorOverloader = ListConcatenationOverloader()
// 复杂的列表操作
return parser.parseExpression(
"{'A', 'B'} + {1, 2} + {'X', 'Y'}"
).getValue(context, List::class.java) as List<Any> // [A, B, 1, 2, X, Y]
}
}
6.3 更复杂的操作符重载示例
矩阵运算操作符重载示例
kotlin
// 简单矩阵类
data class Matrix(val data: Array<IntArray>) {
val rows: Int get() = data.size
val cols: Int get() = if (rows > 0) data[0].size else 0
override fun toString(): String {
return data.joinToString("\n") { row ->
row.joinToString(" ", "[", "]")
}
}
}
// 矩阵运算操作符重载器
class MatrixOperatorOverloader : OperatorOverloader {
override fun overridesOperation(
operation: Operation,
left: Any?,
right: Any?
): Boolean {
return (operation == Operation.ADD || operation == Operation.MULTIPLY) &&
left is Matrix && right is Matrix
}
override fun operate(
operation: Operation,
left: Any?,
right: Any?
): Any {
val leftMatrix = left as Matrix
val rightMatrix = right as Matrix
return when (operation) {
Operation.ADD -> addMatrices(leftMatrix, rightMatrix)
Operation.MULTIPLY -> multiplyMatrices(leftMatrix, rightMatrix)
else -> throw UnsupportedOperationException("Unsupported operation: $operation")
}
}
private fun addMatrices(a: Matrix, b: Matrix): Matrix {
require(a.rows == b.rows && a.cols == b.cols) {
"Matrix dimensions must match for addition"
}
val result = Array(a.rows) { IntArray(a.cols) }
for (i in 0 until a.rows) {
for (j in 0 until a.cols) {
result[i][j] = a.data[i][j] + b.data[i][j]
}
}
return Matrix(result)
}
private fun multiplyMatrices(a: Matrix, b: Matrix): Matrix {
require(a.cols == b.rows) {
"First matrix columns must equal second matrix rows"
}
val result = Array(a.rows) { IntArray(b.cols) }
for (i in 0 until a.rows) {
for (j in 0 until b.cols) {
for (k in 0 until a.cols) {
result[i][j] += a.data[i][j] * b.data[k][j]
}
}
}
return Matrix(result)
}
}
@Service
class MatrixService {
fun performMatrixOperations(): String {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.operatorOverloader = MatrixOperatorOverloader()
// 创建矩阵
val matrixA = Matrix(arrayOf(intArrayOf(1, 2), intArrayOf(3, 4)))
val matrixB = Matrix(arrayOf(intArrayOf(5, 6), intArrayOf(7, 8)))
context.setVariable("A", matrixA)
context.setVariable("B", matrixB)
// 矩阵加法
val sum = parser.parseExpression("#A + #B")
.getValue(context, Matrix::class.java)
return "Matrix Addition Result:\n$sum"
}
}
7. 实际业务场景应用 🏢
让我们看看这些操作符在实际业务中的应用:
7.1 电商系统中的价格计算
kotlin
@Service
class PricingService {
fun calculateFinalPrice(order: Order, customer: Customer): PriceCalculation {
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setVariable("order", order)
context.setVariable("customer", customer)
// 基础价格计算
val basePrice = parser.parseExpression(
"#order.items.![price * quantity].sum()"
).getValue(context, Double::class.java)
// 折扣条件判断
val discountRate = parser.parseExpression(
"#customer.isVip ? 0.1 : (#order.amount > 500 ? 0.05 : 0)"
).getValue(context, Double::class.java)
// 运费计算
val shippingFee = parser.parseExpression(
"#order.amount > 200 ? 0 : 15"
).getValue(context, Double::class.java)
// 最终价格
context.setVariable("basePrice", basePrice)
context.setVariable("discountRate", discountRate)
context.setVariable("shippingFee", shippingFee)
val finalPrice = parser.parseExpression(
"#basePrice * (1 - #discountRate) + #shippingFee"
).getValue(context, Double::class.java)
return PriceCalculation(basePrice, discountRate, shippingFee, finalPrice)
}
}
data class PriceCalculation(
val basePrice: Double,
val discountRate: Double,
val shippingFee: Double,
val finalPrice: Double
)
7.2 用户权限验证系统
kotlin
@Component
class PermissionValidator {
@Value("#{user.role eq 'ADMIN' or (user.role eq 'MANAGER' and user.department eq 'IT')}")
private val hasSystemAccess: Boolean = false
fun