面试题:Spring AOP底层实现原理

一、AOP与IOC的关系:AOP是IOC的扩展功能

AOP 并非独立运行的技术模块,本质是依托 Spring IOC 容器才能生效的扩展能力。

核心结论:先有 IOC,再有 AOP,AOP 完全依托 Bean 生命周期扩展点 BeanPostProcessor 实现

  • BeanPostProcessor 是 Spring 预留的 Bean 生命周期钩子,允许在 Bean 实例初始化前后,对 Bean 对象进行修改、包装、替换操作。
  • AOP 核心增强逻辑由此实现:目标 Bean 完成创建初始化后,Spring 校验当前类是否匹配切面切点;若需要增强,就通过动态代理生成代理对象,直接替换掉原始目标 Bean,最终把代理对象存入 IOC 容器。

这也是我们通过 @Autowired 注入 Bean 时,拿到的不是原始本尊对象,而是代理对象的根本原因。

二、Spring AOP底层实现完整流程拆解

1. 代理创建:解析切面、切点、增强通知

容器启动阶段,Spring 提前完成 AOP 规则扫描与解析:

  1. 扫描项目所有 @Aspect 切面类;
  2. 解析五大增强通知,同时适配 @Transactional 等基于 AOP 实现的注解;
  3. 解析切点表达式,匹配需要增强的类和方法;
  4. 筛选出所有需要被动态代理增强的目标 Bean。

2. 代理选择:JDK 动态代理 VS CGLIB 动态代理

Spring 根据目标类有无接口,自动选择代理方案:

代理方式 适用场景 核心原理 存在限制
JDK动态代理 目标类实现接口 基于接口生成代理类,依靠 InvocationHandler 拦截方法 只能代理有接口的类
CGLIB动态代理 普通无接口类 通过继承目标类生成子类,重写方法完成拦截增强 无法代理 final 类、final 方法

Spring 内置 CGLIB,无接口类默认使用 CGLIB 代理。

3. 方法拦截入口

调用容器中的 Bean 方法时,不会直接执行本尊业务代码,会被代理拦截:

  • CGLIB 代理:拦截入口为 DynamicAdvisedInterceptor#intercept
  • JDK 动态代理:拦截入口为 InvocationHandler#invoke

两种拦截入口,对应手写动态代理的拦截逻辑,是所有AOP增强的统一入口。

4. 组装拦截器链

Spring 不会单独执行每一个通知,而是将当前方法匹配到的所有切面增强,统一整合为拦截器链

  • 正常流程:前置通知 → 环绕前置 → 目标方法 → 环绕后置 → 返回通知 → 最终后置通知;
  • 异常流程:目标方法报错,触发异常通知,终止正常后置流程;
  • 多切面、多通知场景,依靠拦截器链保证执行顺序可控。

5. 拦截器链递归执行

Spring 底层提供统一顶级接口 MethodInvocation,针对两种代理区分不同实现类,互不混用

  • JDK 动态代理:使用 ReflectiveMethodInvocation
  • CGLIB 动态代理:使用 CglibMethodInvocation

共同运行机制:

  1. 内部维护执行索引,默认从 -1 开始;
  2. 底层不断调用原生 proceed() 方法,递归放行执行下一个拦截器;
  3. 所有前置通知执行完毕后,才会调用本尊目标方法
  4. 目标方法执行完成,逆向执行所有后置通知,最后返回结果。

关键知识点(面试易错纠正)

  1. 业务代码中,只有 @Around 环绕通知 能使用 ProceedingJoinPoint 调用 proceed(),手动控制目标方法执行;

  2. @Before / @After 等普通通知,仅能使用 JoinPoint,没有放行方法;

  3. 普通通知的目标方法调用、拦截器链放行,全部由 Spring 底层自动完成,开发者无法干预。

三、面试高频核心问答

1. 同类内部 this 调用,AOP 为什么失效?

this 指向原始目标本尊对象 ,不是容器中的代理对象,

内部调用会直接绕过代理拦截与拦截器链,AOP、事务全部失效。

解决方式:容器上下文获取代理 Bean、自身注入自我调用。

2. Spring AOP 为什么依赖动态代理?

AOP 核心是无侵入增强 ,不修改业务代码;

动态代理可以在运行时创建代理对象,拦截方法执行,在前后追加通用逻辑,是 AOP 唯一底层实现方案。

3. Spring AOP 五大通知及执行顺序

  • @Before:前置通知
  • @Around:环绕通知(权限最高)
  • @AfterReturning:正常返回通知
  • @AfterThrowing:异常通知
  • @After:最终后置通知

标准顺序:
@Before → 环绕前置 → 目标方法 → 环绕后置 → @AfterReturning/@AfterThrowing → @After

四、整体核心流程总结

  1. 规则解析:容器启动扫描切面、切点、通知,匹配待增强 Bean;
  2. 代理替换 :借助 BeanPostProcessor,创建 JDK/CGLIB 代理,替换本尊 Bean 存入容器;
  3. 方法拦截:调用代理对象方法,进入各自代理的拦截入口;
  4. 链路组装:整合所有切面通知,生成专属拦截器链;
  5. 递归执行 :通过 MethodInvocation 实现类递归调度,执行通知、调用本尊方法,完成AOP增强。
相关推荐
超梦dasgg9 分钟前
Java 生产环境 MQ 技术选型全解析
java·开发语言·java-rocketmq·java-rabbitmq
霸道流氓气质9 分钟前
Spring AI 多工具链式调用(Tool Chain)极简实战
java·人工智能·spring
罗超驿44 分钟前
22.深入剖析JDBC架构:从原生API到企业级数据交互核心
java·数据库·mysql·面试
一直有一个ac的梦想1 小时前
cmu15445 2025fall lec 18 transactions with two-phase lock
java·开发语言·数据库
九皇叔叔1 小时前
Spring-Ai-Alibaba [04] 04-llm-platform-custom-demo
java·人工智能·spring
技术路上的探险家1 小时前
Sa-Token 单点登录(SSO)三种模式大白话详解:告别重复登录
java·sa-token·单点登录·sso
JAVA社区1 小时前
Java进阶全套教程(四)—— SpringMVC框架详解
java·开发语言·spring·面试·职场和发展
ㄣ知冷煖★1 小时前
统一网关架构实践:从 Token 鉴权到路由、策略与凭证池转发全链路解析
java·服务器·架构
Lumbrologist1 小时前
【C++】零基础入门 · 第 2 节:变量、基本数据类型与输入输出
java·开发语言·c++
GISer_Jing1 小时前
Three.JS渲染架构解读
java·javascript·架构