跳到正文
Joeplover
学习笔记·2026-05-15·约 3 分钟阅读

AOPDEMO:Spring Boot AOP 切面示例

Spring Boot AOP 基础示例,演示 @Aspect 和切入点表达式。

切面编程

AOPDEMO:Spring Boot AOP 切面编程示例

什么是 AOP

AOP(面向切面编程)的核心思想是:在不修改业务代码的前提下,在方法执行前后插入横切逻辑。业务代码不需要知道切面的存在,切面也不侵入业务逻辑。

Spring AOP 基于动态代理实现:

  • JDK 动态代理:目标类实现接口时使用,代理接口方法
  • CGLIB 代理:目标类没有接口时使用,生成子类代理

这个区别在实际工作中影响很大——如果你用 @Transactional 注解在非接口方法上,默认走 CGLIB,需要额外配置才能生效。

通知类型

Spring 支持 6 种通知类型:

@Aspect
@Component
public class LoggingAspect {
    
    // @Before:方法执行前
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint jp) {
        log.info("方法调用: {}", jp.getSignature());
    }
    
    // @After:方法执行后(无论成功还是异常)
    @After("execution(* com.example.service.*.*(..))")
    public void logAfter() {
        log.info("方法结束");
    }
    
    // @AfterReturning:方法成功返回后
    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void logReturn(Object result) {
        log.info("返回结果: {}", result);
    }
    
    // @AfterThrowing:方法抛出异常后
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void logException(Exception ex) {
        log.error("方法异常", ex);
    }
    
    // @Around:最强大的通知,可完全控制方法执行
    @Around("@annotation(com.example.annotation.Monitor)")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();  // 执行目标方法
        } finally {
            long cost = System.currentTimeMillis() - start;
            log.info("执行耗时: {}ms", cost);
            if (cost > 1000) {
                // 超过 1 秒的慢查询告警
                alertService.sendSlowQueryAlert(pjp.getSignature().toShortString(), cost);
            }
        }
    }
}

@Around 的应用场景

@Around 是唯一一个可以完全控制方法调用的通知类型——可以在目标方法前/后插入逻辑、修改返回值、捕获异常、甚至跳过方法执行。常用场景:

  1. 性能监控:记录方法执行时间,慢查询告警
  2. 缓存:先查缓存,命中直接返回,不执行方法
  3. 重试:方法异常时自动重试
  4. 限流:超出阈值直接返回错误
@Around("@annotation(rateLimiter)")
public Object rateLimit(ProceedingJoinPoint pjp, RateLimiter rateLimiter) throws Throwable {
    String key = rateLimiter.key();
    if (!redisTemplate.opsForValue().setIfAbsent(key, "1", 1, TimeUnit.SECONDS)) {
        throw new RateLimitException("请求太频繁");
    }
    return pjp.proceed();
}

切入点表达式

execution(修饰符 返回类型 包.类.方法(参数))

常用模式:

表达式匹配目标
execution(* com.example.service.*.*(..))service 包下所有方法
execution(* com.example..*.*(..))com.example 及其子包
@annotation(com.example.annotation.Monitor)标注了 @Monitor 的方法
within(com.example.service.*)service 包下的所有类

最大坑:自调用

同类中方法 A 调用方法 B,B 上的 AOP 不生效:

@Service
public class UserService {
    @Transactional
    public void createUser(User user) { ... }
    
    public void batchCreate(List<User> users) {
        for (User user : users) {
            this.createUser(user); // @Transactional 不生效!
        }
    }
}

原因:this 指向原始对象,不是代理对象。AOP 增强只对代理对象上的方法调用生效。

解决方案:

// 方法1:注入自身代理
@Service
public class UserService {
    @Autowired
    private UserService self; // 注入代理对象
    
    public void batchCreate(List<User> users) {
        for (User user : users) {
            self.createUser(user); // 通过代理调用,AOP 生效
        }
    }
}

// 方法2:使用 AopContext
public void batchCreate(List<User> users) {
    for (User user : users) {
        ((UserService) AopContext.currentProxy()).createUser(user);
    }
}

实际应用

在我的项目中,AOP 的主要应用:

  1. 接口性能监控:@Around 记录所有 API 接口的响应时间,超过阈值自动告警
  2. Redis 缓存:@Cacheable 内置基于 AOP 实现
  3. 参数校验:@Before 对敏感接口做二次校验
  4. 操作日志:记录关键业务操作(谁做了什么、参数、结果)