kfyty725/loveqq-framework的AOP通知:@Around注解使用技巧
一、AOP与@Around注解基础
1.1 AOP核心概念
AOP(Aspect-Oriented Programming,面向切面编程)是一种通过横切关注点分离来提高代码模块化的技术。在loveqq-framework中,AOP主要通过以下核心组件实现:
| 组件 | 作用 |
|---|---|
| Join Point(连接点) | 程序执行过程中的特定点,如方法调用或异常抛出 |
| Pointcut(切入点) | 匹配连接点的表达式,用于定义哪些方法需要被增强 |
| Advice(通知) | 切面在特定连接点执行的动作,包括@Around、@Before、@After等 |
| Aspect(切面) | 包含切入点和通知的模块化单元 |
1.2 @Around注解的独特价值
@Around是功能最强大的通知类型,它可以:
- 完全控制目标方法的执行(决定是否执行、何时执行、执行前后的逻辑)
- 修改目标方法的参数和返回值
- 捕获并处理目标方法抛出的异常
与其他通知类型的对比:
二、@Around注解的实现原理
2.1 核心类结构
loveqq-framework的AOP模块通过以下类实现@Around注解功能:
关键代码实现(AspectJMethodAroundAdvice.java):
@Override
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Signature signature = pjp.getSignature();
return this.invokeAdviceMethod(((MethodSignature) signature).getMethod(), pjp, null, null);
}
2.2 执行流程
三、@Around注解实战技巧
3.1 基础用法:性能监控切面
@Aspect
@Component
public class PerformanceMonitorAspect {
private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorAspect.class);
@Around("execution(* com.kfyty.service.*Service.*(..))")
public Object monitorPerformance(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
try {
// 执行目标方法
return pjp.proceed();
} finally {
long costTime = System.currentTimeMillis() - startTime;
logger.info("Method [{}] executed in {}ms",
pjp.getSignature().getName(), costTime);
// 性能告警
if (costTime > 1000) {
logger.warn("Method [{}] execution exceeds 1s",
pjp.getSignature().getName());
}
}
}
}
3.2 参数修改与结果增强
@Around("execution(* com.kfyty.service.UserService.query(..)) && args(id)")
public Object enhanceQuery(ProceedingJoinPoint pjp, Long id) throws Throwable {
// 参数验证与修改
if (id == null || id <= 0) {
throw new IllegalArgumentException("用户ID必须为正整数");
}
// 执行目标方法
User user = (User)pjp.proceed(new Object[]{id});
// 结果增强
if (user != null) {
user.setLastQueryTime(new Date());
user.setQueryCount(user.getQueryCount() + 1);
}
return user;
}
3.3 异常处理与恢复机制
@Around("execution(* com.kfyty.service.PaymentService.process(..))")
public Object handlePaymentException(ProceedingJoinPoint pjp) throws Throwable {
PaymentRequest request = (PaymentRequest)pjp.getArgs()[0];
int retryCount = 3;
while (retryCount-- > 0) {
try {
return pjp.proceed();
} catch (NetworkException e) {
logger.warn("支付请求网络异常,剩余重试次数: {}, 请求ID: {}",
retryCount, request.getRequestId(), e);
if (retryCount == 0) {
// 记录失败请求,以便后续处理
paymentFailureQueue.offer(request);
throw new ServiceUnavailableException("支付服务暂时不可用,请稍后重试");
}
Thread.sleep(1000 * (4 - retryCount)); // 指数退避策略
}
}
throw new IllegalStateException("支付处理失败");
}
3.4 条件执行:动态开关控制
@Around("@annotation(com.kfyty.annotation.FeatureSwitch) && @annotation(switch)")
public Object featureSwitchControl(ProceedingJoinPoint pjp, FeatureSwitch sw) throws Throwable {
String featureKey = sw.value();
boolean enabled = featureSwitchService.isEnabled(featureKey);
if (enabled) {
return pjp.proceed();
}
if (sw.fallbackMethod().isEmpty()) {
return sw.defaultValue();
}
// 调用 fallback 方法
return invokeFallbackMethod(pjp, sw.fallbackMethod());
}
四、高级特性与最佳实践
4.1 与其他通知的协同使用
@Aspect
@Component
public class ComprehensiveAspect {
@Before("execution(* com.kfyty.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
logger.info("方法调用前: {}", joinPoint.getSignature().getName());
}
@Around("execution(* com.kfyty.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
logger.info("方法执行时间: {}ms", System.currentTimeMillis() - startTime);
}
}
@AfterReturning("execution(* com.kfyty.service.*.*(..))")
public void logAfterReturning(JoinPoint joinPoint) {
logger.info("方法调用成功: {}", joinPoint.getSignature().getName());
}
}
执行顺序:@Around前置逻辑 → @Before → 目标方法 → @Around后置逻辑 → @AfterReturning
4.2 性能优化建议
-
缩小切入点范围:
// 不佳 @Around("execution(* com.kfyty..*(..))") // 推荐 @Around("execution(* com.kfyty.service.order.OrderService.createOrder(..))") -
避免在通知中执行耗时操作:
- 复杂逻辑异步处理
- 避免循环和递归调用
-
使用编译时增强:
<plugin> <groupId>org.aspectj</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.9.7</version> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin>
4.3 常见陷阱与解决方案
| 问题 | 解决方案 |
|---|---|
忘记调用pjp.proceed() | 始终确保在@Around通知中调用proceed()方法 |
多次调用proceed() | 使用if (!proceeded) { result = pjp.proceed(); proceeded = true; } |
| 参数修改无效 | 使用pjp.proceed(newArgs)传递修改后的参数 |
| 异常吞噬 | 始终记录异常并根据需要重新抛出 |
五、总结与扩展
@Around注解作为loveqq-framework AOP模块中最强大的通知类型,为开发者提供了全方位的方法增强能力。通过灵活运用@Around,可以实现性能监控、异常处理、功能开关、缓存控制等多种横切关注点功能。
5.1 典型应用场景回顾
5.2 未来扩展方向
- 响应式编程支持:结合loveqq-mvc-reactor模块,提供响应式AOP支持
- 分布式追踪集成:与SkyWalking、Zipkin等分布式追踪系统无缝集成
- AI辅助优化:基于运行时数据自动优化切入点和通知逻辑
通过掌握@Around注解的使用技巧,开发者可以充分发挥AOP的威力,编写更简洁、更健壮、更易于维护的代码。建议在实际项目中结合具体业务场景,灵活运用本文介绍的各种技巧,以达到最佳效果。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



