Appearance
Spring AOP Advisor API 深度解析 🎯
什么是 Advisor?为什么需要它?
在 Spring AOP 的世界里,Advisor 是一个非常重要但经常被误解的概念。让我们先从一个生活化的例子开始理解。
NOTE
想象你是一家餐厅的老板,你需要在特定的时间(比如晚餐高峰期)对特定的服务员(比如新手服务员)进行特殊的指导。这里的"特定时间+特定服务员"就是切点(Pointcut),而"特殊指导"就是通知(Advice)。将这两者结合起来的完整方案,就是 Advisor。
核心概念理解
kotlin
// Advisor = Pointcut + Advice 的完整组合
interface Advisor {
fun getAdvice(): Advice // 要执行的通知逻辑
// 隐含:还包含了何时何地执行的规则(Pointcut)
}
Advisor 解决了什么痛点? 🤔
在没有 Advisor 之前,我们面临以下问题:
kotlin
// 问题:Advice 和 Pointcut 分离,难以管理
class UserService {
fun createUser(user: User) {
// 业务逻辑被各种横切关注点污染
logBefore("创建用户开始")
validateSecurity()
// 真正的业务逻辑
userRepository.save(user)
logAfter("创建用户结束")
sendNotification()
}
}
kotlin
// Advisor 将切点和通知完美结合
@Component
class UserServiceLoggingAdvisor : DefaultPointcutAdvisor() {
init {
// 定义切点:UserService 的所有公共方法
pointcut = AspectJExpressionPointcut().apply {
expression = "execution(* com.example.UserService.*(..))"
}
// 定义通知:方法执行前后的日志记录
advice = object : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any? {
val methodName = invocation.method.name
println("🚀 开始执行: $methodName")
val result = invocation.proceed()
println("✅ 完成执行: $methodName")
return result
}
}
}
}
Advisor 的工作原理 ⚙️
让我们通过时序图来理解 Advisor 在 Spring AOP 中的工作流程:
常用的 Advisor 类型 📚
1. DefaultPointcutAdvisor - 最常用的选择
kotlin
@Configuration
class AopConfig {
@Bean
fun loggingAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor().apply {
// 设置切点:匹配所有 Service 类的方法
pointcut = AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.*.*(..))"
}
// 设置通知:方法拦截器
advice = LoggingMethodInterceptor()
}
}
}
class LoggingMethodInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any? {
val startTime = System.currentTimeMillis()
val methodName = "${invocation.thisObject?.javaClass?.simpleName}.${invocation.method.name}"
try {
println("📊 [$methodName] 开始执行...")
val result = invocation.proceed()
val duration = System.currentTimeMillis() - startTime
println("✅ [$methodName] 执行成功,耗时: ${duration}ms")
return result
} catch (ex: Exception) {
val duration = System.currentTimeMillis() - startTime
println("❌ [$methodName] 执行失败,耗时: ${duration}ms,错误: ${ex.message}")
throw ex
}
}
}
2. 多种 Advice 类型的组合使用
TIP
Spring 允许在同一个 AOP 代理中混合使用不同类型的 Advisor 和 Advice,这提供了极大的灵活性。
kotlin
@Configuration
class ComprehensiveAopConfig {
// 方法执行前的安全检查
@Bean
fun securityAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.UserService.delete*(..))"
},
SecurityBeforeAdvice()
)
}
// 方法执行环绕的性能监控
@Bean
fun performanceAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.*.*(..))"
},
PerformanceMethodInterceptor()
)
}
// 异常处理
@Bean
fun exceptionAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.*.*(..))"
},
ExceptionThrowsAdvice()
)
}
}
完整的 Advice 实现示例
kotlin
// 前置通知:安全检查
class SecurityBeforeAdvice : MethodBeforeAdvice {
override fun before(method: Method, args: Array<out Any>?, target: Any?) {
val currentUser = getCurrentUser()
if (!currentUser.hasPermission("DELETE")) {
throw SecurityException("用户无删除权限")
}
println("🔒 安全检查通过:${currentUser.username}")
}
private fun getCurrentUser(): User {
// 模拟获取当前用户
return User("admin", listOf("DELETE"))
}
}
// 环绕通知:性能监控
class PerformanceMethodInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any? {
val startTime = System.nanoTime()
val methodName = invocation.method.name
return try {
val result = invocation.proceed()
val duration = (System.nanoTime() - startTime) / 1_000_000
if (duration > 100) { // 超过100ms的慢方法
println("⚠️ 慢方法警告: $methodName 耗时 ${duration}ms")
} else {
println("⚡ $methodName 执行完成,耗时: ${duration}ms")
}
result
} catch (ex: Exception) {
println("💥 $methodName 执行异常: ${ex.message}")
throw ex
}
}
}
// 异常通知:异常处理
class ExceptionThrowsAdvice : ThrowsAdvice {
fun afterThrowing(method: Method, args: Array<out Any>?, target: Any?, ex: Exception) {
println("🚨 方法 ${method.name} 抛出异常: ${ex.javaClass.simpleName} - ${ex.message}")
// 可以在这里进行异常上报、邮件通知等
when (ex) {
is IllegalArgumentException -> {
println("📝 参数异常,请检查输入参数")
}
is RuntimeException -> {
println("⚠️ 运行时异常,可能需要人工介入")
}
}
}
}
data class User(val username: String, val permissions: List<String>) {
fun hasPermission(permission: String): Boolean = permissions.contains(permission)
}
实际业务场景应用 🏢
让我们看一个完整的电商订单处理系统,展示 Advisor 如何解决实际业务问题:
kotlin
// 订单服务
@Service
class OrderService(
private val orderRepository: OrderRepository,
private val inventoryService: InventoryService,
private val paymentService: PaymentService
) {
fun createOrder(orderRequest: OrderRequest): Order {
// 纯粹的业务逻辑,不被横切关注点污染
val order = Order(
userId = orderRequest.userId,
items = orderRequest.items,
totalAmount = calculateTotal(orderRequest.items)
)
return orderRepository.save(order)
}
fun cancelOrder(orderId: Long) {
val order = orderRepository.findById(orderId)
?: throw OrderNotFoundException("订单不存在: $orderId")
order.status = OrderStatus.CANCELLED
orderRepository.save(order)
}
private fun calculateTotal(items: List<OrderItem>): BigDecimal {
return items.sumOf { it.price * it.quantity.toBigDecimal() }
}
}
kotlin
// 综合的 AOP 配置
@Configuration
@EnableAspectJAutoProxy
class OrderAopConfig {
// 订单操作审计日志
@Bean
fun orderAuditAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.OrderService.*(..))"
},
OrderAuditAdvice()
)
}
// 订单取消权限检查
@Bean
fun orderCancelSecurityAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.OrderService.cancelOrder(..))"
},
OrderCancelSecurityAdvice()
)
}
// 订单操作性能监控
@Bean
fun orderPerformanceAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.OrderService.*(..))"
},
OrderPerformanceAdvice()
)
}
}
Advisor 的最佳实践 🌟
1. 合理的职责分离
IMPORTANT
每个 Advisor 应该只关注一个特定的横切关注点,避免在单个 Advisor 中处理多种不相关的逻辑。
kotlin
// ✅ 好的做法:职责单一
@Bean
fun cachingAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
expression = "execution(* com.example.service.*Service.get*(..))"
},
CachingMethodInterceptor() // 只处理缓存逻辑
)
}
// ❌ 不好的做法:职责混乱
class MessyAdvice : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any? {
// 同时处理日志、缓存、安全检查...
logMethod(invocation)
checkSecurity(invocation)
val cachedResult = getFromCache(invocation)
// ... 职责不清晰
}
}
2. 切点表达式的精确性
kotlin
// ✅ 精确的切点表达式
@Bean
fun userServiceLoggingAdvisor(): DefaultPointcutAdvisor {
return DefaultPointcutAdvisor(
AspectJExpressionPointcut().apply {
// 只匹配 UserService 的公共方法,排除 getter/setter
expression = "execution(public * com.example.service.UserService.*(..)) " +
"&& !execution(* get*()) && !execution(* set*())"
},
LoggingMethodInterceptor()
)
}
3. 异常处理的完整性
WARNING
在 Advisor 中处理异常时,要确保不会意外吞掉重要的异常信息。
kotlin
class RobustMethodInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any? {
val methodName = invocation.method.name
return try {
println("开始执行: $methodName")
val result = invocation.proceed()
println("成功执行: $methodName")
result
} catch (businessEx: BusinessException) {
// 业务异常:记录但继续抛出
println("业务异常 in $methodName: ${businessEx.message}")
throw businessEx
} catch (systemEx: Exception) {
// 系统异常:记录详细信息后抛出
println("系统异常 in $methodName: ${systemEx.javaClass.name} - ${systemEx.message}")
throw systemEx
}
}
}
总结 📝
Spring 的 Advisor API 为我们提供了一种优雅的方式来组织和管理横切关注点:
核心价值
- 职责分离:将业务逻辑与横切关注点完全分离
- 灵活组合:可以灵活地组合不同的 Pointcut 和 Advice
- 统一管理:通过 Advisor 统一管理切面逻辑的应用规则
- 易于测试:每个关注点都可以独立测试
记住这个核心公式
Advisor = Pointcut + Advice
它不仅仅是技术组件的组合,更是一种设计哲学的体现:在正确的时间、正确的地点,做正确的事情。
通过合理使用 Advisor,我们可以构建出既强大又优雅的企业级应用,让代码更加清晰、可维护,同时保持良好的性能和扩展性。 🚀