Skip to content

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