侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

Spring AOP 中常用通知注解的作用及简单实现

2025-12-13 / 0 评论 / 4 阅读

题目

Spring AOP 中常用通知注解的作用及简单实现

信息

  • 类型:问答
  • 难度:⭐

考点

AOP基本概念,通知类型,切点表达式

快速回答

Spring AOP 提供了五种常用通知注解:

  • @Before:目标方法执行前执行
  • @After:目标方法执行后执行(无论是否异常)
  • @AfterReturning:目标方法成功返回后执行
  • @AfterThrowing:目标方法抛出异常后执行
  • @Around:包裹目标方法执行(可控制是否执行目标方法)

示例:使用 @Around 记录方法执行时间:

@Around("execution(* com.example.service.*.*(..))")
public Object logTime(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = pjp.proceed(); // 执行目标方法
    long time = System.currentTimeMillis() - start;
    System.out.println("方法执行耗时:" + time + "ms");
    return result;
}
## 解析

一、原理说明

Spring AOP(面向切面编程)通过代理模式在运行时动态织入横切逻辑(如日志、事务等)。核心概念:

  • 切面(Aspect):封装横切关注点的模块(使用 @Aspect 注解的类)
  • 通知(Advice):切面中具体的执行逻辑(五种注解对应不同执行时机)
  • 切点(Pointcut):定义通知应用的连接点(通过表达式指定)

二、通知类型详解与代码示例

1. 基础通知类型

@Aspect
@Component
public class LoggingAspect {

    // 1. @Before 示例
    @Before("execution(* com.example.service.UserService.*(..))")
    public void beforeAdvice(JoinPoint jp) {
        System.out.println("准备执行:" + jp.getSignature().getName());
    }

    // 2. @After 示例
    @After("serviceMethods()")
    public void afterAdvice(JoinPoint jp) {
        System.out.println("方法执行结束:" + jp.getSignature().getName());
    }

    // 3. @AfterReturning 示例
    @AfterReturning(
        pointcut = "serviceMethods()",
        returning = "result"
    )
    public void afterReturning(Object result) {
        System.out.println("方法返回:" + result);
    }

    // 4. @AfterThrowing 示例
    @AfterThrowing(
        pointcut = "serviceMethods()",
        throwing = "ex"
    )
    public void afterThrowing(Exception ex) {
        System.out.println("方法异常:" + ex.getMessage());
    }

    // 切点复用定义
    @Pointcut("execution(* com.example.service.*.*(..))")
    private void serviceMethods() {}
}

2. @Around 高级示例(方法耗时监控)

@Around("serviceMethods()")
public Object monitorTime(ProceedingJoinPoint pjp) throws Throwable {
    long startTime = System.currentTimeMillis();

    try {
        // 执行目标方法(可在此处决定是否调用)
        Object result = pjp.proceed();
        return result;
    } finally {
        long duration = System.currentTimeMillis() - startTime;
        String methodName = pjp.getSignature().toShortString();
        System.out.println(methodName + " 执行耗时: " + duration + "ms");
    }
}

三、最佳实践

  • 切点表达式优化:使用 @Pointcut 定义可重用的切点表达式
  • 性能考虑:避免在通知中执行耗时操作,尤其在高频方法上
  • 异常处理@Around 中调用 pjp.proceed() 需处理异常或重新抛出
  • 最小作用域:精确限定切点范围(如 execution(* com.example.service.*.*(..)) 优于宽泛表达式)

四、常见错误

  • 错误1:忘记在配置类添加 @EnableAspectJAutoProxy
  • 错误2:切点表达式错误导致通知未触发(如包路径拼写错误)
  • 错误3:在 @Around 中未调用 proceed() 导致目标方法未执行
  • 错误4:对非 Spring 管理的对象(如 private 方法)使用 AOP

五、扩展知识

  • 代理机制:Spring AOP 默认使用 JDK 动态代理(基于接口)或 CGLIB(基于类)
  • 执行顺序:多个切面作用于同一方法时,可通过 @Order 注解控制顺序
  • 限制:Spring AOP 仅支持方法级别的拦截,不支持字段拦截
  • 进阶:了解 AspectJ 可实现更强大的切点表达式(如 @within, @annotation