Skip to content

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,我们可以构建出既强大又优雅的企业级应用,让代码更加清晰、可维护,同时保持良好的性能和扩展性。 🚀