顾乔芝士网

持续更新的前后端开发技术栈

Spring Boot AOP 最佳实践指南_spring-boot-starter-aop

Spring Boot AOP 最佳实践指南

Spring AOP(面向切面编程,Aspect-Oriented Programming)是 Spring 框架的重要组成部分,用于将日志、事务、监控等横切关注点从业务逻辑中抽离出来。Spring Boot 进一步简化了 AOP 的集成和配置。


1. 添加依赖

在 Spring Boot 项目中,只需添加 AOP 依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

如果需要使用注解驱动的事务(@Transactional),还会用到:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

2. AOP 核心概念回顾

  • Aspect(切面):封装横切逻辑的模块
  • Join Point(连接点):程序运行过程中的点(如方法调用)
  • Advice(通知):在连接点执行的操作(前置、后置、异常、环绕)
  • Pointcut(切点):匹配连接点的规则
  • Weaving(织入):将切面应用到目标对象的过程

3. Spring AOP 的代理机制

Spring AOP 基于代理实现(默认是运行时代理):

  • JDK 动态代理:如果目标类实现了接口,则默认使用 JDK 动态代理(只能代理接口方法)
  • CGLIB 代理:如果目标类没有实现接口,Spring 使用 CGLIB 生成子类代理

注意:

  • private 和 final 方法无法被代理
  • 自调用(同类方法内部调用)不会触发 AOP

4. 启用 AOP 配置

Spring Boot 默认会开启 AOP,但有时我们需要更精细的控制:

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
public class AopConfig {
}
  • exposeProxy = true:允许通过 AopContext.currentProxy() 获取当前代理对象,解决自调用问题
  • proxyTargetClass = true:强制使用 CGLIB 代理(即使有接口)

5. 定义切面与通知

@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("调用方法: " + joinPoint.getSignature().getName() +
                           " 参数: " + Arrays.toString(joinPoint.getArgs()));
    }

    @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
    public void logAfterReturning(Object result) {
        System.out.println("方法返回值: " + result);
    }

    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void logAfterThrowing(Exception ex) {
        System.out.println("方法抛出异常: " + ex.getMessage());
    }

    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();
            long cost = System.currentTimeMillis() - start;
            System.out.println(pjp.getSignature() + " 执行耗时: " + cost + "ms");
            return result;
        } catch (Throwable ex) {
            System.out.println("方法执行异常: " + ex.getMessage());
            throw ex;
        }
    }
}

6. 使用自定义注解增强

创建一个注解,用来标记需要监控的方法:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

切面:

@Aspect
@Component
public class LogExecutionTimeAspect {
    
    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long cost = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " 执行耗时: " + cost + "ms");
        return proceed;
    }
}

使用:

@Service
public class UserService {
    @LogExecutionTime
    public void longRunningTask() {
        // 模拟耗时任务
        Thread.sleep(1000);
    }
}

7. 实战场景

7.1 统一日志打印

可在切面中统一收集 请求参数、返回值、耗时,避免在业务代码里写重复的日志。

7.2 性能监控

结合 Prometheus、Micrometer 等工具,将方法耗时指标上报到监控系统。

7.3 权限校验

通过 AOP 拦截 @RequiresPermission 注解的方法,在执行前做权限验证。

7.4 异常处理

在 Service 层拦截异常,统一包装成业务异常对象返回。


8. 性能与最佳实践

  1. 保持切面简洁:切面只处理横切关注点,不要写复杂业务逻辑
  2. 避免过度使用:AOP 不适合所有场景,能用 Spring MVC/Security/拦截器解决的尽量用框架本身
  3. 高性能场景下注意开销:日志/监控类切面可异步化(如写入消息队列)
  4. 切面顺序:多个切面时可用 @Order 指定优先级
  5. 调试技巧:确认切点表达式正确,可以用 JoinPoint 打印方法信息

9. 常见问题

  1. AOP 不生效的原因
  • Bean 未被 Spring 管理(缺少 @Component)
  • 方法是 private / final
  • 自调用未经过代理
  • 切点表达式错误
  1. 解决自调用问题
  • 使用 AopContext.currentProxy() 获取代理调用
  • 或者拆分逻辑到其他 Bean

总结:
Spring Boot 对 AOP 的支持非常完善,只需引入依赖、编写切面类即可。合理使用 AOP 可以极大提升 日志监控、性能统计、权限控制 等横切关注点的复用性和可维护性。


控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言