← 每日面试题 学习看板

介绍一下Spring的AOP

面试题/00java/04 框架/01Spring/01Spring核心机制/介绍一下Spring的AOP.md

面试记录 2026-09-28

题目:介绍一下Spring的AOP

难度:difficulty/medium

标签:spring/core, general/knowledge, difficulty/medium

评分:⭐⭐⭐☆☆(3/5)

我的回答

Spring AOP,AOP 就是基于代理的切面编程增强。它的核心逻辑就是在创建对象的时候,创建的是一个代理对象。在你的对象的前后,对应标注的方法,增加了对应的代理方法实现。在调用你的方法前,先调用代理内的前置方法、后置方法或者环绕方法、前后环绕方法,最终实现对你原有的方法的增强。我们常规用来使用的 AOP 会很多,比如健全,大量方法的健全使用一个注解,然后在健全,在进入方法前提前进行校验健全就可以了。另外就是日志,调用的使用环绕的 AOP 切面,在调用前调用后记录对应的日志。这是 AOP 的常规使用方式,另外还有一些打点可能会用到 AOP。

点评

优点:

不足:

补充:

核心概念:

通知类型:

AOP 五大失效场景:

  1. 类内部自调用:this.aMethod() 不走代理 → 事务失效、日志不生效
  2. 方法 private / final / static:代理无法覆写
  3. 方法非 public:Spring AOP 基于代理,非 public 方法可能被忽略
  4. 目标类未注册为 Spring Bean:无代理对象
  5. 切面 bean 没被 Spring 扫描:切点无匹配对象

规避自调用失效方案:

完整讲解

第一部分:知识重点剖析

核心考点定位:考察对 AOP 设计思想、Spring AOP 实现机制以及工程实践中失效场景的深度理解。

知识点拆解:

  1. AOP 思想本源:
  1. 七大核心概念:
  1. Spring AOP 实现机制:
  1. 切点表达式(Pointcut Expression):
  1. 织入方式对比:
  1. AOP 五大失效场景(工程必看):
  1. 自调用失效的四种规避方案:

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. Spring AOP 和 AspectJ 有什么本质区别?为什么 Spring 不用 AspectJ 做织入?
  1. Spring Boot 2 为什么默认强制 CGLIB?有什么坑?
  1. 如何写一个"脱敏"切面,在方法返回前把敏感字段打码?

第三部分:面试者参考回答(300-600 字口语化)

面试官您好,Spring AOP 本质上是基于动态代理的切面编程增强。它的核心思想是关注点分离——把日志、事务、权限、监控这些横切关注点从业务代码中剥离出来,通过代理对象在方法执行前后插入增强逻辑。

核心概念有七个:JoinPoint(连接点)是程序执行中能插入增强的点,Spring AOP 只支持方法执行;Pointcut(切点)定义哪些连接点需要被增强,通常用 @Pointcut 注解配表达式;Advice(通知)是在连接点做什么,有五种:Before、After、AfterReturning、AfterThrowing、Around,其中 Around 最灵活,能拿到 ProceedingJoinPoint 控制是否执行原方法;Aspect(切面)是切点 + 通知的组合,通常是一个 @Component 类;Target是被代理的原始对象;Introduction让目标类运行时实现新接口;Weaving(织入)是把切面织入连接点。

Spring AOP 的实现机制:底层通过 BeanPostProcessor(AbstractAutoProxyCreator)在 Bean 初始化后判断是否需要代理,需要就包装成代理对象。代理选型有两种:JDK 动态代理基于接口,目标类必须有接口;CGLIB基于继承生成子类覆写方法。Spring 5+ 默认策略是目标类有接口用 JDK、无接口用 CGLIB;Spring Boot 2.x 更进一步,通过 spring.aop.proxy-target-class=true 强制使用 CGLIB,主要是为了解决 JDK 代理下 this 指向代理对象而非目标类导致的类型不一致问题。

切点表达式主要有几种:execution( com.x.service..*(..)) 匹配所有 service 包下的方法(最常用)、@within 匹配被特定注解标注的类、@annotation 匹配被特定注解标注的方法、@args 匹配参数带注解的方法,可以用 && / || / ! 组合。

工程中最容易踩的坑是切面失效,主要是五种场景:

  1. 类内部自调用:this.aMethod() 直接调原方法绕过代理,导致事务、日志、脱敏切面全部失效。规避方案是从其他 Bean 调用、用 AopContext.currentProxy()、@Lazy 自注入 self 字段、或拆成两个 Bean
  2. 方法 private / final / static:代理无法覆写 final 和 static,CGLIB 不能拦截 private
  3. 方法非 public:JDK 动态代理只支持 public 接口方法
  4. 目标类不是 Spring Bean:无代理对象
  5. 切面 bean 没被扫描:切点无匹配

实际场景中我用 AOP 主要做三类事:日志切面用 @Around 记录方法入参、耗时、返回值;权限校验切面用 @Before 校验方法上的自定义 @RequirePermission 注解;脱敏切面用 @Around 遍历返回值对象,把标了 @Sensitive 的字段打码。

需要澄清的一点:Spring AOP 不是真正的字节码织入,它只支持方法级切点,无法切字段访问、构造器、私有方法。如果需要这些能力,得用 AspectJ 的编译期或类加载期织入。