SpringBoot 中的 AOP(Aspect-Oriented Programming,面向切面编程)是一种通过分离横切关注点(如日志、事务、权限校验等)来提高代码复用性和可维护性的编程思想。它可以在不修改原有业务逻辑代码的前提下,对方法进行增强(如前置处理、后置处理、异常处理等)。

一、AOP 核心概念

  1. 切面(Aspect)
    封装横切关注点的类,包含了通知(增强逻辑)和切点(拦截规则)的定义。
    例:@Aspect 注解标记的类就是一个切面。
  2. 通知(Advice)
    切面中具体的增强逻辑,定义了在“何时”执行增强操作。常见类型:

    • @Before:目标方法执行执行
    • @After:目标方法执行执行(无论是否异常)
    • @AfterReturning:目标方法正常返回后执行
    • @AfterThrowing:目标方法抛出异常后执行
    • @Around:环绕目标方法,可自定义执行时机(最灵活,需手动调用目标方法)
  3. 切点(Pointcut)
    定义拦截哪些方法的规则,通过表达式(如 execution)指定目标方法。
    例:execution(* com.example.service.*.*(..)) 表示拦截 com.example.service 包下所有类的所有方法。
  4. 连接点(JoinPoint)
    程序执行过程中可被拦截的点(如方法调用、异常抛出等),是切点的实际执行位置。
  5. 目标对象(Target)
    被切面拦截并增强的原始对象(业务逻辑类)。
    例:execution(* com.example.service.*.*(..)) 表示 com.example.service 包下所有类的所有方法。
  6. 代理对象(Proxy)
    AOP 生成的、包含目标对象和增强逻辑的代理实例,实际执行时通过代理对象调用目标方法。

二、AOP 实现原理

SpringBoot 的 AOP 基于动态代理实现:

  • 若目标类实现了接口:默认使用 JDK 动态代理(通过接口生成代理类)。
  • 若目标类未实现接口:使用 CGLIB 动态代理(通过继承目标类生成代理类)。
    (SpringBoot 2.x 后默认支持 CGLIB,无需额外配置)

三、SpringBoot 中使用 AOP 的步骤

  1. 引入依赖
    pom.xml 中添加 AOP starter(SpringBoot 已整合,无需手动配置代理):

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
  2. 定义切面类

    • @Aspect 标记类为切面。
    • @Component 注册为 Spring 组件。
  3. 定义切点
    @Pointcut 注解配合表达式定义拦截规则,常见表达式:

    • execution:匹配方法执行(最常用),语法:execution(修饰符 返回值 包名.类名.方法名(参数))
      例:execution(* com.example.service.UserService.*(..)) 拦截 UserService 类的所有方法。
    • @annotation:匹配被指定注解标记的方法
      例:@annotation(com.example.annotation.Log) 拦截所有被 @Log 注解标记的方法。
  1. execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
    精确匹配DeptServiceImpl类中public、无返回值、名为delete且参数为一个Integer的方法。
  2. execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
    类似上一条,但不限制方法访问修饰符(public/private/protected等)。
  3. execution(void delete(java.lang.Integer))
    匹配任意类中所有无返回值、名为delete且参数为Integer的方法。省略包名和类名会导致匹配范围过大,不推荐使用。
  4. execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
    匹配DeptServiceImpl类中任意返回类型、名为delete且参数为Integer的方法。
  5. execution(* com.*.service.impl.DeptServiceImpl.delete(java.lang.Integer))
    匹配com包下任意一级子包(如com.foocom.bar)中DeptServiceImpl类的delete方法。
  6. execution(* com.itheima.service.impl.*.delete(java.lang.Integer))
    匹配DeptServiceImpl类所在包中任意类delete方法(参数为Integer)。
  7. execution(* com.itheima.service.impl.*.*(java.lang.Integer))
    匹配DeptServiceImpl类所在包中任意类的任意方法,但要求参数必须是一个Integer
  8. execution(* com.itheima.service.impl.*.*(*))
    匹配DeptServiceImpl类所在包中任意类的任意方法,但要求必须有且仅有一个参数(类型不限)。
  9. execution(* com.itheima.service.impl.*.del*(*))
    匹配DeptServiceImpl类所在包中任意类del开头的方法,且方法必须有一个参数(类型不限)。
  10. execution(* com.itheima.service.impl.*.*e(*))
    匹配DeptServiceImpl类所在包中任意类e结尾的方法,且方法必须有一个参数(类型不限)。
  11. execution(* com..service.impl.DeptServiceImpl.*(..))
    匹配com包下任意层级子包(如com.foo.bar)中DeptServiceImpl类的任意方法(参数不限)。
  12. execution(* com.itheima.service.*.*(..))
    匹配com.itheima.service包下所有类的所有方法(参数不限)。
  13. 组合表达式

    @Before("execution(* com.itheima.service.impl.DeptServiceImpl.list(..)) || " +
            "execution(* com.itheima.service.impl.DeptServiceImpl.delete(..))")

    同时匹配DeptServiceImpl类中的list方法delete方法(任意参数、任意返回类型)。

关键符号总结:

  • *:匹配任意返回类型任意包名/类名/方法名(单个部分)。
  • ..:匹配任意层级子包任意数量、类型的参数
  • ||:逻辑或,组合多个切入点。
  1. 定义通知
    在切面类中用通知注解(如 @Before@AfterReturning 等)定义增强逻辑。

四、示例代码

// 1. 定义切面类
@Aspect
@Component
public class LogAspect {

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

    // 3. 前置通知:方法执行前打印日志
    @Before("servicePointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName(); // 获取方法名
        Object[] args = joinPoint.getArgs(); // 获取方法参数
        System.out.println("方法 " + methodName + " 开始执行,参数:" + Arrays.toString(args));
    }

    // 后置返回通知:方法正常返回后打印结果
    @AfterReturning(pointcut = "servicePointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法 " + methodName + " 执行结束,返回值:" + result);
    }
}

五、AOP 的常见应用场景

  • 日志记录:自动记录方法调用、参数、返回值等。
  • 事务管理:通过 @Transactional 注解(底层基于 AOP)控制事务的提交/回滚。
  • 权限校验:方法执行前验证用户权限,无权限则拦截。
  • 异常处理:统一捕获方法抛出的异常并处理(如返回友好提示)。
  • 性能监控:统计方法执行耗时,分析性能瓶颈。

六、注意事项

  • AOP 拦截的是Spring 容器管理的Bean,非 Spring 管理的对象无法被增强。
  • 同一目标方法被多个切面拦截时,可通过 @Order 注解指定切面执行顺序(值越小越先执行)。
  • @Around 通知需手动调用 ProceedingJoinPoint.proceed() 执行目标方法,否则目标方法不会执行。

通过 AOP,SpringBoot 可以优雅地实现业务逻辑与横切逻辑的解耦,大幅提升代码的可维护性。

最后修改:2025 年 07 月 02 日
如果觉得我的文章对你有用,请随意赞赏