究极迷惑:在学习 Spring AOP 时,我们大多会记住切面、切点、通知这些概念,却始终对运行时到底发生了什么有困惑: 程序进方法时,先进代理对象还是先进原始方法? 为什么 在Debug模式下直接跳进我们写的业务代码,完全看不到代理类? AOP 的 “前置执行、执行目标、后置执行” 到底藏在哪里? 循环、断点、AOP 底层好像隐隐有些什么联系?


一、AOP 执行全过程

  1. 请求进入方法 程序并没有直接进入我们写的方法,而是先进入隐式代理对象

  2. 执行前置增强 在代理内部执行通知中的 “前” 逻辑(日志、权限、耗时统计等)。

  3. 调用目标方法 执行 proceed() → 跳转到原始业务代码

  4. 原始方法执行业务逻辑 我们写的核心代码运行。

  5. 回到代理对象 原始方法执行完,控制权回到代理对象。

  6. 执行后置增强 代理执行 “后” 逻辑。

  7. 返回结果 整个流程结束。

AOP 的底层是动态代理。 代理对象是隐式、无源码、不可见的,像一层 “虚线外壳” 包裹着原始方法。

整个执行流程可以概括为:

  1. 进入方法 → 先进入隐式代理对象

  2. 执行前置通知逻辑

  3. 调用 proceed() → 跳转到我们写的原始业务方法

  4. 原始方法执行完毕 → 回到隐式代理对象

  5. 执行后置通知逻辑

  6. 返回结果

这和计算机底层断点拦截、外层包裹内层、执行完再返回的结构高度一致。


二、AOP 动态代理到底是什么?

AOP 不修改源码,却能在方法前后统一插入逻辑,依靠的就是动态代理

动态代理在运行时由 JVM 自动生成一个全新的代理类,它:

  • 没有 .java 源文件

  • 不会出现在我们的项目结构里

  • Debug 看不到它的代码

  • 但它真实存在于内存中

它的作用只有一个:包裹原始对象,在调用原始方法前后插入增强逻辑


三、执行流程

我们以最常见的 @Around 环绕通知为例:

@Around("execution(* com.xxx.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    // 1. 前置逻辑
    System.out.println("方法执行前");

    // 2. 执行目标方法
    Object result = pjp.proceed();

    // 3. 后置逻辑
    System.out.println("方法执行后");

    return result;
}

内存中真实存在的代理对象(伪代码)

class UserServiceProxy extends UserService {
    @Override
    public Object addUser() {
        // ———————————————— 代理对象执行:前置 ————————————————
        System.out.println("方法执行前");

        // ———————————————— 调用原始方法(Debug跳到这里)————————————
        Object result = super.addUser();

        // ———————————————— 代理对象执行:后置 ————————————————
        System.out.println("方法执行后");

        return result;
    }
}

为什么 Debug 直接跳进原始方法?

代理对象是运行时生成的二进制类,没有源码文件。 IDEA 无法断点进入、无法展示、无法高亮。 执行到 proceed() 时,直接跳到我们能看见的、真正写代码的地方:原始业务方法

即:代理对象是隐式的、逻辑上的、虚线框架,只做包裹,不做可视化展示。


四、循环、嵌套、AOP 底层联系【“断点”】

循环、递归、AOP、代理,底层都是同一套结构:

外层包裹 → 进入内层 → 执行完 → 回到外层

  • for 循环:外层控制 → 内层执行 → 回到外层

  • 递归:外层调用 → 内层进入 → 回到外层

  • AOP:代理包裹 → 原始方法 → 回到代理

结构完全同源

AOP 本质就是方法级别的断点拦截

正常流程: 调用方法 → 直接执行方法

AOP 流程: 调用方法 → 被代理拦截(类似触发断点)→ 执行外部增强逻辑 → 回到原方法继续执行

和断点、拦截、钩子、回调的底层模型完全一致。


五、浅浅总结

  1. AOP 底层 : 动态代理

  2. 代理对象 = 隐式、无源码、内存中存在(逻辑包裹)、Debug 不可见

  3. 执行顺序:代理前 → 原始方法 → 代理后

  4. Debug 跳进原始方法,是因为代理没有源码

  5. AOP 本质 = 方法级断点拦截 + 外层包裹内层

  6. 代理就像一层看不见的壳,包住方法,前后插逻辑

 AOP的底层是动态代理,动态代理生成了代理对象,代理对象整体逻辑与通知中的逻辑相同分为三块,方法执行前,方法执行,方法执行后,程序进入后首先进入的是代理对象的方法执行前;完成方法执行后进入方法执行,会在debug运行模式下直接跳转到了原始的方法代码(此处可以理解为代理对象不会显示呈现,类似于逻辑上的一种隐式,相当于一个虚线框架将这三部分操作逻辑进行一个逻辑意义上的集成,可能计算机内部程序内部会有逻辑记录没有具体实现,不会以可视化代码形式体现,所以本质上在执行方法代码阶段,我们debug进入了原始方法代码,执行的也就是原始代码)执行完原始代码后进入第三块,方法执行后,是在(隐式)的代理对象中进行,这整一个流程类似于计算机底层‘断点’的形式,在正常的程序流程中遇到断点,执行外部程序,回到断点继续执行当前程序,还有循环,嵌套,递归等等的底层也有类似的逻辑结构

AOP不应该仅仅只是停留在注解使用,现在,我们已经摸到了动态代理、内存结构、执行流程、底层模型。AOP 并不神秘: 它就是一个看不见的代理对象,在合适的地方帮我们悄悄插入代码,执行完再把流程还给我们。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐