云计算百科
云计算领域专业知识百科平台

Spring AOP 超全详解:面向切面编程 + 五大通知类型 + 切入点表达式 + ThreadLocal 实战

一、AOP 基础概念

1. 什么是 AOP

AOP(Aspect Oriented Programming)即面向切面编程,核心思想:不修改原有业务代码,对指定方法进行功能增强。 AOP 是一种编程思想,Spring AOP 是该思想在 Spring 框架中的落地实现,底层基于动态代理技术。

2. 核心优势

  • 减少重复代码,通用逻辑统一维护
  • 对业务代码零侵入,不影响原有业务逻辑
  • 提升开发效率,降低后期维护成本

3. 典型应用场景

  • 统计业务方法执行耗时
  • 系统操作日志记录
  • 声明式事务管理(@Transactional底层实现)
  • 接口权限统一校验

二、SpringAOP 快速入门:方法耗时统计

实现步骤

  • pom.xml 引入 AOP 场景依赖
  • 定义切面类,添加@Aspect + @Component注解
  • 编写通知方法,配置切入点表达式,实现增强逻辑
  • 完整代码示例

    1)引入依赖

    <dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-aop</artifactid>
    </dependency>

    2)切面类实现

    @aspect
    @component
    public class recordtimeaspect {

    // 环绕通知 + 切入点表达式:匹配service.impl下所有类的所有方法
    @around("execution(* com.itheima.service.impl.*.*(..))")
    public object recordtime(proceedingjoinpoint pjp) throws throwable {
    // 1. 记录方法开始时间
    long begin = system.currenttimemillis();
    // 2. 执行原始目标方法
    object result = pjp.proceed();
    // 3. 计算并打印耗时
    long end = system.currenttimemillis();
    system.out.println("方法执行耗时:" + (end – begin) + "ms");
    // 返回方法执行结果
    return result;
    }
    }

    三、AOP 五大核心概念

    核心概念说明
    连接点 JoinPoint 所有可以被 AOP 增强的方法(候选方法集合)
    通知 Advice 额外增强的业务逻辑(比如统计耗时、记录日志的代码)
    切入点 PointCut 匹配规则,筛选出真正需要增强的连接点
    切面 Aspect 切面 = 通知 + 切入点,封装增强逻辑与匹配规则
    目标对象 Target 被增强功能的原始业务对象

    底层原理:Spring 基于动态代理自动生成代理对象,替换原始对象注入 IOC 容器;方法调用时实际执行代理对象,先执行增强逻辑,再调用原方法。

    四、五大通知类型详解

    根据执行时机不同,通知分为五类,其中环绕通知功能最强大:

    注解执行时机核心特点
    @Around 环绕通知 目标方法执行前后都可执行 功能最强,需手动调用proceed()触发原方法,必须返回结果
    @Before 前置通知 目标方法执行之前运行 无法阻止方法执行,仅做前置准备
    @After 后置通知 目标方法执行完毕后运行 无论方法正常结束还是抛出异常,都会执行
    @AfterReturning 返回后通知 方法正常返回结果后运行 方法抛出异常则不执行,可获取方法返回值
    @AfterThrowing 异常通知 方法抛出异常后运行 仅异常场景触发,可捕获异常信息

    ⚠️ 关键注意事项:

    • @Around 必须手动调用 pjp.proceed(),否则原始方法不会执行
    • @Around 必须返回Object类型结果,否则接口无法获取返回数据

    五、切入点表达式

    1. execution 表达式(最常用)

    根据包名、类名、方法名批量匹配方法。

    语法格式

    execution(返回值类型 包名.类名.方法名(参数类型))

    通配符说明
    • *:匹配任意返回值、任意类、任意方法名
    • ..:匹配任意类型、任意个数的参数

    // 匹配service.impl包下所有类的所有方法
    execution(* com.itheima.service.impl.*.*(..))

    2. @annotation 注解匹配

    匹配标注了指定自定义注解的方法,粒度更灵活。

    // 匹配所有加了@Log注解的方法
    @around("@annotation(com.itheima.anno.log)")

    适用场景:零散分布的增强需求,比如指定部分接口记录操作日志。

    六、@Pointcut 切入点抽取

    作用

    将重复的切入点表达式抽取为独立方法,实现复用,简化代码。

    代码示例

    // 抽取公共切入点
    @pointcut("execution(* com.itheima.service.impl.*.*(..))")
    private void pt(){}

    // 直接引用切入点方法
    @around("pt()")
    public object recordtime(proceedingjoinpoint pjp) throws throwable {
    // 增强逻辑
    }

    访问权限

    • private:仅当前切面类内部可引用
    • public:其他切面类也可以引用该切入点

    七、JoinPoint 连接点对象

    两类对象区别

    • JoinPoint:适用于前置、后置等普通通知,可获取方法基本信息
    • ProceedingJoinPoint:仅适用于@Around环绕通知,继承自 JoinPoint,额外支持proceed()执行原方法

    常用方法

    // 获取目标类对象
    object target = joinpoint.gettarget();
    // 获取方法签名、方法名
    string methodname = joinpoint.getsignature().getname();
    // 获取方法入参数组
    object[] args = joinpoint.getargs();

    八、ThreadLocal 线程局部变量

    核心特性

    ThreadLocal 不是线程,而是线程级别的局部存储变量,为每个线程提供独立的存储空间,线程之间数据完全隔离,互不干扰。

    核心 API

    方法作用
    set(object value) 向当前线程存入数据
    get() 读取当前线程存储的数据
    remove() 移除当前线程的数据

    典型实战:登录用户信息传递

    一次请求对应一个线程,在过滤器解析登录信息后存入 ThreadLocal,后续 Controller、Service、AOP 均可直接获取,无需层层传参。

    工具类封装

    public class currentholder {
    private static final threadlocal<integer> current_local = new threadlocal<>();

    public static void setcurrentid(integer empid){
    current_local.set(empid);
    }

    public static integer getcurrentid(){
    return current_local.get();
    }

    public static void remove(){
    current_local.remove();
    }
    }

    使用流程
  • Token 过滤器中解析登录 ID,调用setCurrentId()存入
  • AOP/Service 中调用getCurrentId()获取登录人
  • 请求结束前调用remove()清除数据
  • ⚠️ 强制注意:使用完毕必须调用 remove (),否则线程复用会导致数据错乱,引发内存泄漏。


    九、实战案例:操作日志记录

    需求

    将系统中增、删、改接口的操作信息记录到数据库,包含:操作人、操作时间、执行方法、请求参数、返回值、执行时长。

    实现方案

  • 自定义@Log注解,标记需要记录日志的接口
  • 定义日志切面类,@Around环绕通知匹配@Log注解
  • 通过ProceedingJoinPoint获取类名、方法名、请求参数
  • 通过ThreadLocal获取当前登录操作人 ID
  • 计算方法执行耗时,组装日志实体,调用 Mapper 插入数据库

  • 十、开发避坑指南 ⚠️

  • 切面类必须同时加@Aspect和@Component,仅加@Aspect无法被 Spring 管理
  • @Around环绕通知必须调用pjp.proceed(),且必须返回执行结果
  • ThreadLocal 使用后必须手动remove(),防止内存泄漏与数据污染
  • 切入点表达式范围不要过大,避免不必要的增强影响接口性能
  • @After无论异常与否都会执行,需要仅成功场景执行选@AfterReturning
  • 同一个方法被多个切面增强,可通过@Order注解指定执行优先级
  • 十一、文末总结

    AOP 是 Spring 框架三大核心特性之一,通过动态代理实现代码无侵入增强,是企业开发中解耦通用逻辑的核心手段。 掌握五大通知类型、两种切入点表达式写法,结合 ThreadLocal 实现上下文数据传递,即可覆盖绝大多数 AOP 业务场景,也是 Spring 事务、日志、权限等组件的底层基础。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Spring AOP 超全详解:面向切面编程 + 五大通知类型 + 切入点表达式 + ThreadLocal 实战
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!