Spring Boot AOP 全面解析(原理 + 实战 + 场景)

AOP(面向切面编程)是 Spring 核心特性之一,Spring Boot 对 AOP 做了轻量化封装,无需复杂配置即可快速实现日志记录、性能监控、权限校验、事务管理等横切逻辑,大幅提升代码复用性和可维护性。

一、核心概念(快速理解)
概念 说明
切面(Aspect) 封装横切逻辑的类(如日志切面、权限切面),是 AOP 的核心载体
连接点(JoinPoint) 程序执行过程中的任意节点(如方法调用、异常抛出),Spring 仅支持方法级连接点
切入点(Pointcut) 匹配需要增强的连接点(如指定包下的所有方法),通过表达式精准筛选
通知(Advice) 切面的具体增强逻辑,分为 5 种类型:✅ 前置通知(Before):方法执行前执行✅ 后置通知(After):方法执行后执行(无论是否异常)✅ 返回通知(AfterReturning):方法正常返回后执行✅ 异常通知(AfterThrowing):方法抛出异常后执行✅ 环绕通知(Around):包裹方法执行,可自定义执行时机
织入(Weaving) 将切面逻辑融入目标方法的过程,Spring Boot 中默认是运行时织入
二、快速上手(Spring Boot 集成 AOP)
1. 引入依赖

xml

复制代码
<!-- Spring Boot AOP 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 编写切面类(实战:日志记录)

java

运行

复制代码
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

// 1. 标记为切面类 + 交给 Spring 管理
@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    // 2. 定义切入点:匹配 com.example.demo.service 包下所有类的所有方法
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void servicePointcut() {}

    // 3. 前置通知:记录方法调用信息
    @Before("servicePointcut()")
    public void beforeMethod(JoinPoint joinPoint) {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        log.info("【前置通知】调用方法:{}.{},参数:{}", className, methodName, args);
    }

    // 4. 环绕通知:监控方法执行耗时
    @Around("servicePointcut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        // 执行目标方法
        Object result = joinPoint.proceed();
        long costTime = System.currentTimeMillis() - startTime;
        log.info("【环绕通知】方法 {} 执行耗时:{}ms", joinPoint.getSignature().getName(), costTime);
        return result;
    }

    // 5. 返回通知:记录方法返回值
    @AfterReturning(value = "servicePointcut()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result) {
        log.info("【返回通知】方法 {} 返回结果:{}", joinPoint.getSignature().getName(), result);
    }

    // 6. 异常通知:记录方法异常信息
    @AfterThrowing(value = "servicePointcut()", throwing = "e")
    public void afterThrowingMethod(JoinPoint joinPoint, Exception e) {
        log.error("【异常通知】方法 {} 抛出异常:{}", joinPoint.getSignature().getName(), e.getMessage(), e);
    }
}
3. 测试目标方法

java

运行

复制代码
import org.springframework.stereotype.Service;

@Service
public class UserService {
    public String getUserInfo(Long id) {
        // 模拟业务逻辑
        if (id <= 0) {
            throw new IllegalArgumentException("用户ID不能为负数");
        }
        return "用户ID:" + id + ",姓名:张三";
    }
}
4. 运行效果

调用 getUserInfo(1L) 会输出:

plaintext

复制代码
【前置通知】调用方法:com.example.demo.service.UserService.getUserInfo,参数:[1]
【环绕通知】方法 getUserInfo 执行耗时:2ms
【返回通知】方法 getUserInfo 返回结果:用户ID:1,姓名:张三

调用 getUserInfo(-1L) 会输出:

plaintext

复制代码
【前置通知】调用方法:com.example.demo.service.UserService.getUserInfo,参数:[-1]
【异常通知】方法 getUserInfo 抛出异常:用户ID不能为负数
三、进阶用法
1. 切入点表达式(常用)
表达式类型 示例 说明
按方法签名匹配 execution(* com.example.service.*.*(..)) 匹配 service 包下所有类的所有方法
按注解匹配 @annotation(com.example.annotation.Log) 匹配标注了 @Log 注解的方法
按包匹配 within(com.example.service..*) 匹配 service 包及子包下的所有类
2. 自定义注解 + AOP(更灵活)

java

运行

复制代码
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String desc() default ""; // 日志描述
}

// 切面匹配注解
@Pointcut("@annotation(com.example.demo.annotation.Log)")
public void annotationPointcut() {}

// 使用注解
@Service
public class OrderService {
    @Log(desc = "创建订单")
    public String createOrder(String orderNo) {
        return "订单创建成功:" + orderNo;
    }
}
四、注意事项
  1. 代理机制 :Spring AOP 默认使用 JDK 动态代理(针对接口),无接口时自动切换为 CGLIB 代理;可通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用 CGLIB。
  2. 内部方法调用 :目标类内部调用自身方法(如 A.method1() 调用 A.method2()),method2 的切面不会生效(代理对象未被触发),可通过注入自身 Bean 解决。
  3. 性能影响:AOP 基于动态代理实现,少量切面对性能影响可忽略;大量复杂环绕通知需注意优化逻辑。
  4. 优先级 :多个切面可通过 @Order(n) 指定执行顺序,n 越小优先级越高。
五、典型应用场景
  1. 日志审计:记录接口调用的请求参数、响应结果、操作人、操作时间。
  2. 性能监控:统计接口响应时间,识别慢接口。
  3. 权限控制:校验用户是否有接口访问权限。
  4. 事务管理 :通过 @Transactional(底层基于 AOP)控制事务边界。
  5. 缓存控制:缓存方法返回结果,减少重复计算。
  6. 参数校验:统一校验接口入参的合法性。
相关推荐
原来是好奇心4 小时前
深入Spring Boot源码(三):自动配置之Spring Boot的“魔法“核心
java·自动配置·源码·springboot
学网安的肆伍4 小时前
【038-安全开发篇】JavaEE应用&SpringBoot框架&MyBatis注入&Thymeleaf模版注入
spring boot·安全·java-ee
努力努力再努力wz4 小时前
【Linux网络系列】:网络+网络编程(UDPsocket+TCPsocket)
java·linux·c语言·开发语言·数据结构·c++·centos
占疏4 小时前
流程图编辑
java·数据库·sql
heartbeat..4 小时前
Java List 完全指南:从接口特性到四大实现类深度解析
java·list
韩立学长4 小时前
【开题答辩实录分享】以《智慧酒店管理——手机预订和住宿管理》为例进行选题答辩实录分享
android·java·后端
何中应4 小时前
【面试题-8】Spring/Spring MVC/Spring Boot/Spring Cloud
java·spring boot·后端·spring·mvc·面试题
坐不住的爱码4 小时前
mybatis-动态sql语句-<foreach>
java·sql·mybatis
while(1){yan}4 小时前
HTTP的数据报格式
java·开发语言·网络·网络协议·http·青少年编程·面试