Appearance
Spring Expression Language (SpEL) - Collection Selection 集合选择
🎯 什么是 Collection Selection?
Collection Selection(集合选择)是 Spring Expression Language (SpEL) 中一个强大的功能,它允许我们从一个源集合中筛选出符合特定条件的元素,并创建一个新的集合。
TIP
想象一下你在一个大型图书馆中寻找特定类型的书籍。Collection Selection 就像是一个智能的图书管理员,能够根据你的条件(比如"作者是中国人"或"出版年份在2020年之后")快速筛选出符合要求的书籍列表。
🤔 为什么需要 Collection Selection?
在实际开发中,我们经常遇到这样的场景:
- 从用户列表中筛选出活跃用户
- 从订单列表中找出今天的订单
- 从商品列表中筛选出特定价格范围的商品
- 从配置项中选择启用的功能
如果没有 Collection Selection,我们需要编写大量的循环和条件判断代码:
kotlin
// 传统方式:需要手动循环筛选
fun filterActiveUsers(users: List<User>): List<User> {
val activeUsers = mutableListOf<User>()
for (user in users) {
if (user.isActive && user.lastLoginDays < 30) {
activeUsers.add(user)
}
}
return activeUsers
}
kotlin
// 使用 SpEL Collection Selection:一行搞定
val activeUsers = parser.parseExpression(
"users.?[isActive && lastLoginDays < 30]"
).getValue(context) as List<User>
📝 基本语法
Collection Selection 使用以下语法模式:
.?[selectionExpression] // 选择所有符合条件的元素
.^[selectionExpression] // 选择第一个符合条件的元素
.$[selectionExpression] // 选择最后一个符合条件的元素
IMPORTANT
?
表示选择所有匹配的元素^
表示选择第一个匹配的元素$
表示选择最后一个匹配的元素
🛠️ 实际应用示例
1. 用户管理系统
kotlin
@Service
class UserService {
private val parser = SpelExpressionParser()
data class User(
val name: String,
val age: Int,
val department: String,
val isActive: Boolean,
val salary: Double
)
fun demonstrateCollectionSelection() {
val users = listOf(
User("张三", 25, "技术部", true, 8000.0),
User("李四", 30, "销售部", false, 6000.0),
User("王五", 28, "技术部", true, 9000.0),
User("赵六", 35, "人事部", true, 7000.0)
)
val context = StandardEvaluationContext().apply {
setVariable("users", users)
}
// 筛选技术部的活跃用户
val techActiveUsers = parser.parseExpression(
"#users.?[department == '技术部' && isActive]"
).getValue(context) as List<User>
println("技术部活跃用户: ${techActiveUsers.map { it.name }}")
// 输出: [张三, 王五]
// 找出工资最高的用户(选择第一个)
val highestSalaryUser = parser.parseExpression(
"#users.^[salary > 8500]"
).getValue(context) as User?
println("高薪用户: ${highestSalaryUser?.name}")
// 输出: 王五
}
}
2. 电商订单处理
kotlin
@RestController
@RequestMapping("/api/orders")
class OrderController {
private val parser = SpelExpressionParser()
data class Order(
val id: String,
val customerId: String,
val amount: Double,
val status: String,
val createTime: LocalDateTime
)
@GetMapping("/filter")
fun filterOrders(@RequestParam status: String?): ResponseEntity<List<Order>> {
val orders = getOrdersFromDatabase() // 假设从数据库获取订单
val context = StandardEvaluationContext().apply {
setVariable("orders", orders)
setVariable("targetStatus", status ?: "COMPLETED")
}
// 使用 SpEL 筛选指定状态的订单
val filteredOrders = parser.parseExpression(
"#orders.?[status == #targetStatus]"
).getValue(context) as List<Order>
return ResponseEntity.ok(filteredOrders)
}
@GetMapping("/high-value")
fun getHighValueOrders(): ResponseEntity<List<Order>> {
val orders = getOrdersFromDatabase()
val context = StandardEvaluationContext().apply {
setVariable("orders", orders)
}
// 筛选高价值订单(金额大于1000)
val highValueOrders = parser.parseExpression(
"#orders.?[amount > 1000 && status == 'COMPLETED']"
).getValue(context) as List<Order>
return ResponseEntity.ok(highValueOrders)
}
private fun getOrdersFromDatabase(): List<Order> {
// 模拟数据库数据
return listOf(
Order("001", "user1", 1500.0, "COMPLETED", LocalDateTime.now().minusDays(1)),
Order("002", "user2", 800.0, "PENDING", LocalDateTime.now().minusHours(2)),
Order("003", "user1", 2000.0, "COMPLETED", LocalDateTime.now().minusDays(3))
)
}
}
3. Map 集合的选择操作
kotlin
@Component
class ConfigurationManager {
private val parser = SpelExpressionParser()
fun demonstrateMapSelection() {
// 系统配置映射
val systemConfig = mapOf(
"maxUsers" to 100,
"timeout" to 30,
"retryCount" to 3,
"bufferSize" to 1024,
"debugLevel" to 2
)
val context = StandardEvaluationContext().apply {
setVariable("config", systemConfig)
}
// 筛选值小于50的配置项
val smallValueConfigs = parser.parseExpression(
"#config.?[value < 50]"
).getValue(context) as Map<String, Int>
println("小值配置项: $smallValueConfigs")
// 输出: {timeout=30, retryCount=3, debugLevel=2}
// 获取第一个值大于500的配置项
val firstLargeConfig = parser.parseExpression(
"#config.^[value > 500]"
).getValue(context) as Map.Entry<String, Int>?
println("第一个大值配置: ${firstLargeConfig?.key} = ${firstLargeConfig?.value}")
// 输出: bufferSize = 1024
}
}
🔄 集合选择的工作流程
🎯 支持的集合类型
Collection Selection 支持多种集合类型:
集合类型 | 说明 | 示例 |
---|---|---|
Array | 数组 | array.?[condition] |
List | 列表 | list.?[condition] |
Set | 集合 | set.?[condition] |
Map | 映射 | map.?[key == 'target'] |
Iterable | 可迭代对象 | iterable.?[condition] |
NOTE
对于 Map 类型,筛选表达式会针对每个 Map.Entry
对象进行评估,你可以使用 key
和 value
属性来访问键值对。
⚡ 性能优化建议
性能注意事项
Collection Selection 会遍历整个集合,对于大型集合可能存在性能问题。
kotlin
@Service
class OptimizedCollectionService {
private val parser = SpelExpressionParser()
// ❌ 不推荐:对大集合进行复杂筛选
fun inefficientFiltering(largeUserList: List<User>): List<User> {
val context = StandardEvaluationContext().apply {
setVariable("users", largeUserList) // 假设有10万条数据
}
return parser.parseExpression(
"#users.?[isActive && department == '技术部' && salary > 8000]"
).getValue(context) as List<User>
}
// ✅ 推荐:先在数据库层面筛选,再使用 SpEL 进行细粒度过滤
fun efficientFiltering(): List<User> {
// 先通过数据库查询减少数据量
val preFilteredUsers = userRepository.findActiveUsersByDepartment("技术部")
val context = StandardEvaluationContext().apply {
setVariable("users", preFilteredUsers) // 数据量已大幅减少
}
return parser.parseExpression(
"#users.?[salary > 8000]"
).getValue(context) as List<User>
}
}
🔒 安全集合选择
SpEL 还支持安全导航操作符,防止空指针异常:
kotlin
// 安全集合选择 - 即使 users 为 null 也不会抛异常
val safeResult = parser.parseExpression(
"users?.?[isActive]"
).getValue(context) as List<User>?
📚 总结
Collection Selection 是 SpEL 中的一个强大功能,它能够:
✅ 简化代码:用一行表达式替代复杂的循环逻辑
✅ 提高可读性:筛选条件直观明了
✅ 支持多种集合:Array、List、Set、Map 等
✅ 灵活筛选:支持复杂的条件表达式
✅ 安全操作:支持安全导航,避免空指针异常
最佳实践
- 对于大型集合,优先在数据源层面进行预筛选
- 合理使用
^
和$
操作符获取首个或末个匹配元素 - 结合安全导航操作符
?.
提高代码健壮性 - 在 Spring Boot 应用中,可以将常用的筛选表达式配置化管理
通过掌握 Collection Selection,你可以让集合操作变得更加优雅和高效! 🚀