【面试突击】深度进阶:Spring AOP 代理机制、失效场景与切面顺序全解析

【面试突击七】深度进阶:Spring AOP 代理机制、失效场景与切面顺序全解析

如果说 IOC 是 Spring 的骨架,那么 AOP(面向切面编程)就是 Spring 的灵魂。它让我们能够在不修改源代码的情况下,优雅地解耦日志、事务、权限等通用业务。

本文将带你攻克 AOP 的三大难点:代理选择机制、失效场景判定、多切面执行顺序


一、 AOP 底层双雄:JDK vs CGLIB

Spring AOP 的本质是动态代理。根据目标对象是否实现接口,Spring 会在运行期动态选择以下两种技术之一。

1. 核心对比

特性JDK 动态代理CGLIB 动态代理
实现原理基于 Java 反射 机制基于 ASM 字节码 框架
代理形式生成接口的实现类(匿名类)生成目标类的子类(继承)
目标要求目标类 必须实现接口目标类不能是 final
关系比喻代理类与目标类是 兄弟 关系代理类是目标类的 儿子
配置参数proxyTargetClass = false (默认)proxyTargetClass = true

2. Spring 怎么选?

  • 默认策略:如果目标 Bean 实现了接口,用 JDK;否则用 CGLIB。
  • 现代趋势:Spring Boot 2.x 开始,默认配置 spring.aop.proxy-target-class=true,即默认全量使用 CGLIB。这样做是为了减少因接口缺失导致的代理失败,同时也避免了 JDK 代理在某些场景下的类型强转异常。

二、 避坑指南:为什么我的 AOP 失效了?

在开发中,明明写了切面,逻辑却没执行,通常是以下四个原因导致的:

1. “内部调用”陷阱(最常见)

场景:类 A 里面有方法 method1method2method2 上有切面。当你在 method1 中通过 this.method2() 调用时,切面失效。

  • 原因:AOP 拦截的是代理对象的调用。在方法内部,this 指向的是原始对象,而不是增强后的代理对象。
  • 图解
    外部调用 -> [代理对象.method1] -> [原始对象.method1] 
                                            |
                                            |--直接调--> [原始对象.method2] (切面跳过!)
    
  • 解决:使用 AopContext.currentProxy() 获取当前代理对象调用,或者重构代码将方法拆分到不同的 Bean。

2. 方法权限问题

  • 原因:JDK 代理只能代理接口中的 public 方法;CGLIB 代理无法重写 privatefinalstatic 方法。

3. 对象不是 Spring Bean

  • 原因:AOP 代理是在 Bean 初始化阶段(后置处理器)生成的。如果你直接 new 了一个对象,Spring 根本无法介入。

三、 切面执行顺序:洋葱模型

当一个方法被多个切面(如事务、日志、缓存)同时拦截时,谁先执行?

1. 顺序控制

  • @Order(n) 注解:数值 n 越小,优先级越高(越在洋葱的最外层)。
  • Ordered 接口:重写 getOrder() 方法,返回小数值者优先。

2. 执行流程图解

想象一个洋葱,高优先级的切面(Order 越小)包裹在最外面:

       进入方法 (Request) >>>             <<< 退出方法 (Response)
    ___________________________________________________________
   |  Aspect 1 (Order=1)                                       |
   |    [ @Before ]                                            |
   |   _____________________________________________________   |
   |  |  Aspect 2 (Order=10)                                |  |
   |  |    [ @Before ]                                      |  |
   |  |   _______________________________________________   |  |
   |  |  |           目标业务方法执行 (Target)            |  |  |
   |  |  |_______________________________________________|  |  |
   |  |                                                     |  |
   |  |    [ @After / @AfterReturning ]                     |  |
   |  |_____________________________________________________|  |
   |                                                           |
   |    [ @After / @AfterReturning ]                           |
   |___________________________________________________________|
  • 入站阶段Order 越小,@Before 越先执行。
  • 出站阶段Order 越小,@After 越后执行(最后离开战场)。

四、 AOP 术语极简记忆

为了方便面试复述,请牢记这五个词:

  1. Aspect (切面):你要写的“通用逻辑”类(如日志类)。
  2. Pointcut (切入点):你要拦截的“具体范围”(如 execution(* ...))。
  3. Advice (通知):你打算“什么时候”干活(Before / After / Around)。
  4. Join Point (连接点):程序运行中的“拦截位置”(Spring 中仅支持方法调用)。
  5. Proxy (代理):Spring 最终为你生成的那个“全能助手”。

五、 总结建议

  • 优先考虑职责分离:如果遇到内部调用失效,考虑业务逻辑是否需要拆分。
  • 掌握代理原理:JDK 看接口(兄弟),CGLIB 看继承(父子)。
  • 关注顺序逻辑:涉及事务和日志时,通常日志的 Order 应该比事务更小,以便记录事务开启和结束的完整过程。

作者提示:AOP 的强大在于无侵入,但过度使用会增加调试难度。建议仅在处理横切关注点时使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值