Spring AOP

xiaopeng
发布于 2026-08-18 / 1 阅读
0
0

Spring AOP

Spring AOP 与动态代理 ⭐⭐⭐⭐⭐

面试频率:★★★★★
工作频率:★★★★★


⚡30 秒速记(复习只看这里)

🟢 一句话

AOP 就是在不修改原业务代码的情况下,通过代理对象在方法执行前后增加额外逻辑。

Spring AOP 核心:

原始对象
   ↓
Spring创建代理对象
   ↓
调用代理对象
   ↓
执行额外逻辑
   ↓
调用原始方法

常见应用:

@Transactional

@Async

日志

权限

监控

🟡 Spring AOP 核心原理

Spring Bean
   ↓
BeanPostProcessor
   ↓
判断是否需要AOP
   ↓
创建代理对象
   ↓
Spring容器保存代理对象
   ↓
@Autowired拿到代理对象

🔴 @Transactional 为什么能生效?

因为:

调用代理对象
   ↓
事务拦截器
   ↓
开启事务
   ↓
调用原始方法
   ↓
成功 → commit
异常 → rollback

所以:

真正实现事务的是代理对象,不是 @Transactional 注解自己。


📚 一、什么是 AOP?

AOP:

Aspect Oriented Programming

中文:

面向切面编程。

不用死背英文。

简单理解:

在不修改业务代码的情况下,在方法执行前后增加额外功能。


例如:

原来:

public void createOrder() {

    saveOrder();

}

现在需要:

记录日志。

最简单的方法:

public void createOrder() {

    System.out.println("开始");

    saveOrder();

    System.out.println("结束");

}

这样确实可以。

但是如果:

100个Service

1000个方法

全部需要:

日志

事务

权限

监控

每个方法都写一遍:

非常麻烦。


所以使用 AOP:

业务代码:

public void createOrder() {

    saveOrder();

}

额外功能:

日志
事务
权限
监控

统一放到外面处理。


二、AOP 核心思想 ⭐⭐⭐⭐⭐

假设:

OrderService

原来:

调用者

↓

OrderService

↓

createOrder()

使用 AOP:

调用者
   ↓
代理对象
   ↓
额外逻辑
   ↓
OrderService
   ↓
createOrder()
   ↓
额外逻辑

例如事务:

调用者
   ↓
代理对象
   ↓
开启事务
   ↓
createOrder()
   ↓
提交事务

所以:

AOP 的核心就是代理。


三、什么是动态代理? ⭐⭐⭐⭐⭐

假设原始对象:

OrderService

Spring 会给它生成:

OrderService代理对象

代理对象内部最终会调用:

真正的OrderService

所以:

orderService.createOrder();

看起来:

只是调用一个普通方法。

实际上可能是:

调用代理对象

↓

执行额外逻辑

↓

调用真正的createOrder()

↓

执行额外逻辑

例如:

OrderService代理对象
        ↓
    开启事务
        ↓
OrderService.createOrder()
        ↓
    提交事务

四、@Transactional 为什么能工作? ⭐⭐⭐⭐⭐

例如:

@Service
public class OrderService {

    @Transactional
    public void createOrder() {

        saveOrder();

        deductStock();

    }

}

我们只写了:

createOrder();

但是 Spring 可以做到:

开启事务

↓

createOrder()

↓

成功

↓

commit

发生异常:

开启事务

↓

createOrder()

↓

异常

↓

rollback

原因:

Spring 创建了代理对象。

原始对象:

OrderService


Spring最终提供:

OrderService代理对象

调用:

orderService.createOrder();

实际流程:

调用代理对象
     ↓
事务拦截器
     ↓
开启事务
     ↓
调用原始对象
     ↓
createOrder()
     ↓
   成功?
  ↙    ↘
是      否
↓       ↓
commit rollback

所以:

@Transactional

本身只是告诉Spring:

这个方法需要事务。

真正执行:

开启事务

提交事务

回滚事务

的是:

Spring 的事务拦截器 / 代理机制。


五、AOP 和 Bean 生命周期的关系 ⭐⭐⭐⭐⭐

你前面学过:

实例化

↓

属性填充

↓

Aware

↓

BeanPostProcessor

↓

初始化

↓

BeanPostProcessor

↓

Bean完成

Spring AOP:

和:

BeanPostProcessor

密切相关。

可以先这样理解:

new OrderService()
        ↓
原始Bean
        ↓
Bean生命周期
        ↓
BeanPostProcessor处理
        ↓
发现需要AOP
        ↓
创建代理对象
        ↓
最终Spring Bean

所以:

@Autowired
private OrderService orderService;

拿到的:

可能并不是最开始:

new OrderService()

得到的原始对象。

而可能是:

OrderService代理对象

六、AOP 和三级缓存的关系

你前面学过循环依赖:

三级缓存

singletonFactories

保存:

获取Bean提前引用的ObjectFactory

为什么不能简单地:

直接把原始 Bean 给别人?

因为:

Bean 最终可能需要:

AOP代理

例如:

@Transactional

最终:

原始A

↓

A的代理对象

如果循环依赖的时候:

B拿到原始A

但是其他地方:

拿到A的代理对象

就可能出现引用不一致。

所以:

三级缓存的 ObjectFactory:

可以在需要提前获取 Bean 引用时:

获取提前引用

↓

必要时考虑代理处理

↓

返回合适的提前引用

这也是:

三级缓存和 AOP 关系密切的原因之一。


七、Spring 两种动态代理 ⭐⭐⭐⭐⭐

Spring AOP 常见:

JDK动态代理

CGLIB代理

八、JDK 动态代理

JDK 动态代理:

主要基于:

接口

例如:

public interface OrderService {

    void createOrder();

}

实现:

public class OrderServiceImpl
        implements OrderService {

    @Override
    public void createOrder() {

    }

}

Spring 可以:

OrderService接口
       ↑
       |
   代理对象
       |
       ↓
OrderServiceImpl

简单理解:

JDK 动态代理主要通过接口生成代理对象。


九、CGLIB 动态代理

如果:

没有接口。

例如:

@Service
public class OrderService {

    public void createOrder() {

    }

}

可以通过:

CGLIB

生成:

OrderService的子类

类似:

OrderService
     ↑
     |
OrderService代理子类

代理子类:

重写方法。

然后:

加入额外逻辑。


例如:

原来:

createOrder()

代理之后可以理解:

createOrder() {

    开启事务();

    super.createOrder();

    提交事务();

}

当然实际源码比这个复杂很多。

这里只需要理解原理。


十、JDK 动态代理和 CGLIB 区别 ⭐⭐⭐⭐⭐

JDK 动态代理

主要基于:

接口

代理:

接口

CGLIB

主要基于:

继承

生成:

目标类的子类

速记:

JDK

↓

接口


CGLIB

↓

继承 / 子类

十一、为什么 final 方法不好代理?

因为:

CGLIB 通过:

继承目标类

↓

重写方法

实现代理。

但是:

public final void save() {

}

final 方法:

不能被子类重写。

所以:

CGLIB 无法通过重写这个方法来进行正常代理增强。


同理:

private void save() {

}

private 方法:

子类也不能重写。

所以:

不能像普通可覆盖方法一样进行代理增强。


十二、为什么 private 方法的 @Transactional 通常不生效? ⭐⭐⭐⭐⭐

例如:

@Transactional
private void saveOrder() {

}

事务依赖:

Spring AOP代理

代理需要:

拦截方法调用。

但是:

private方法

不能像普通可代理方法一样被代理拦截。

所以:

@Transactional
private void saveOrder()

通常不会按你预期产生事务增强。


简单记:

@Transactional

↓

依赖代理

↓

private方法无法正常被代理增强

↓

事务不生效

十三、最经典的坑:自己调用自己 ⭐⭐⭐⭐⭐

例如:

@Service
public class OrderService {

    public void createOrder() {

        save();

    }


    @Transactional
    public void save() {

        // 保存订单

    }

}

看起来:

createOrder()

↓

save()

↓

@Transactional

↓

开启事务

但是:

可能不生效。


为什么?

因为:

save();

实际上相当于:

this.save();

属于:

对象内部调用。


正常事务调用:

外部调用

↓

代理对象

↓

事务拦截器

↓

save()

↓

原始对象

但是内部调用:

外部
 ↓
代理对象
 ↓
createOrder()
 ↓
原始对象
 ↓
this.save()

这里:

this.save()

直接调用当前对象的方法。

没有重新经过:

代理对象

所以:

事务拦截器没有执行

最终:

@Transactional

可能不生效

十四、自调用为什么绕过代理?

假设:

Spring 给:

OrderService

创建:

OrderServiceProxy

正常:

Controller

↓

OrderServiceProxy.save()

↓

事务

↓

OrderService.save()

但是:

public void createOrder() {

    this.save();

}

流程:

Controller

↓

OrderServiceProxy.createOrder()

↓

OrderService.createOrder()

↓

this.save()

↓

OrderService.save()

注意:

最后:

OrderService.save()

没有经过:

OrderServiceProxy

所以:

代理无法拦截。


十五、@Async 也是类似原理

例如:

@Async
public void sendEmail() {

}

Spring:

同样可以通过代理机制:

调用代理对象

↓

拦截@Async

↓

把任务提交线程池

↓

异步线程执行

所以:

@Transactional

@Async

AOP日志

虽然功能完全不同:

但底层都大量使用:

Spring代理机制

十六、AOP 常见使用场景

1. 事务

@Transactional

2. 日志

例如:

方法开始

↓

记录参数

↓

执行方法

↓

记录耗时

3. 权限

例如:

请求方法

↓

检查用户权限

↓

有权限

↓

执行方法

4. 接口监控

例如:

开始时间

↓

执行方法

↓

结束时间

↓

计算耗时

5. @Async

方法调用

↓

代理拦截

↓

提交线程池

↓

异步执行

十七、几个 AOP 常见概念

面试可能会问:

切面 Aspect

切点 Pointcut

通知 Advice

连接点 JoinPoint

不用一开始背得特别复杂。


Aspect(切面)

可以理解:

一整套 AOP 逻辑。

例如:

日志切面

负责:

所有日志相关增强。


Pointcut(切点)

表示:

哪些方法需要被增强。

例如:

com.xxx.service下面所有方法

Advice(通知)

表示:

具体增加什么逻辑。

例如:

方法执行前记录日志

方法执行后记录日志

JoinPoint(连接点)

可以简单理解:

可以被 AOP 增强的方法执行位置。


速记:

Aspect

↓

我要做一套什么增强


Pointcut

↓

我要增强哪些方法


Advice

↓

我要增加什么代码

十八、AOP 完整流程 ⭐⭐⭐⭐⭐

例如:

@Transactional
public void createOrder() {

}

可以理解成:

Spring启动
    ↓
创建OrderService
    ↓
实例化原始Bean
    ↓
属性填充
    ↓
初始化
    ↓
BeanPostProcessor处理
    ↓
发现需要AOP增强
    ↓
创建代理对象
    ↓
Spring容器管理代理对象
    ↓
@Autowired注入代理对象
    ↓
调用createOrder()
    ↓
代理拦截
    ↓
开启事务
    ↓
调用原始createOrder()
    ↓
   是否异常?
   ↙      ↘
没有      有
 ↓         ↓
commit   rollback

十九、和之前知识串起来 ⭐⭐⭐⭐⭐

现在 Spring 这一条线:

Spring Bean生命周期
        ↓
      实例化
        ↓
     属性填充
        ↓
     初始化
        ↓
BeanPostProcessor
        ↓
       AOP
        ↓
    创建代理对象
        ↓
@Transactional / @Async

循环依赖:

Bean实例化
    ↓
提前暴露
    ↓
三级缓存
    ↓
必要时考虑AOP代理
    ↓
解决部分循环依赖

所以:

Bean生命周期

循环依赖

三级缓存

AOP

@Transactional

这些知识:

其实全部是连在一起的。


🎤 面试回答(30秒)

Spring AOP 是面向切面编程,核心思想是在不修改业务代码的情况下,通过代理对象给方法增加事务、日志、权限等额外功能。

Spring AOP 主要使用 JDK 动态代理和 CGLIB 代理。JDK 动态代理主要基于接口,而 CGLIB 通过生成目标类的子类实现代理。

像 @Transactional 就是典型的 AOP 应用,Spring 创建代理对象,在调用目标方法之前开启事务,方法成功后提交,异常时根据事务规则进行回滚。


🎯 面试追问

Q1:什么是 AOP?

答:

在不修改原业务代码的情况下,通过代理机制在方法执行前后增加额外功能。


Q2:Spring AOP 怎么实现?

答:

主要通过:

动态代理

常见:

JDK动态代理

CGLIB

Q3:JDK 和 CGLIB 有什么区别?

答:

JDK动态代理

↓

主要基于接口


CGLIB

↓

通过继承目标类生成代理子类

Q4:@Transactional 为什么能实现事务?

答:

Spring 会通过 AOP 创建代理对象。

调用事务方法时:

代理对象

↓

开启事务

↓

调用原始方法

↓

提交 / 回滚

Q5:@Transactional 注解自己会开启事务吗?

答:

不会。

注解主要是:

事务配置标记

真正处理事务的是:

Spring事务拦截器

+

代理机制

Q6:为什么 private 方法的 @Transactional 通常不生效?

答:

因为 Spring 声明式事务依赖 AOP 代理,而 private 方法不能像普通可代理方法一样被代理拦截增强。


Q7:为什么同一个类里面调用 @Transactional 方法可能失效?

答:

因为:

this.save();

属于对象内部调用。

没有经过:

Spring代理对象

所以事务拦截器没有执行。


Q8:BeanPostProcessor 和 AOP 什么关系?

答:

Spring AOP 的代理创建和 BeanPostProcessor 机制密切相关,Bean 创建过程中可以通过后置处理器判断 Bean 是否需要增强,并创建代理对象。


Q9:AOP 有哪些使用场景?

答:

事务

日志

权限

监控

异步

⚠️ 工作踩坑

1. 同类自调用导致事务失效

例如:

public void createOrder() {

    save();

}

@Transactional
public void save() {

}

这里:

this.save()

没有经过代理。

所以:

事务可能不生效。


2. private 方法加 @Transactional

例如:

@Transactional
private void save() {

}

不要指望它通过普通 Spring AOP 代理正常产生事务增强。


3. 自己 new Service

例如:

OrderService service =
        new OrderService();

这个对象:

不是 Spring 容器提供的代理 Bean。

所以:

@Transactional

@Async

AOP

等 Spring 代理能力可能不会生效。

正确:

让 Spring 管理:

@Autowired
private OrderService orderService;

4. 看到 @Transactional 就认为一定有事务

错误。

应该想到:

@Transactional
      ↓
是否Spring Bean?
      ↓
是否经过代理?
      ↓
方法是否可被正常代理?
      ↓
调用是否绕过代理?

❓ 自测

  1. 什么是 AOP?

  2. AOP 解决什么问题?

  3. Spring AOP 的核心实现原理是什么?

  4. 什么是动态代理?

  5. JDK 动态代理和 CGLIB 有什么区别?

  6. @Transactional 为什么可以实现事务?

  7. @Transactional 注解自己会开启事务吗?

  8. Spring Bean 为什么可能不是原始对象?

  9. BeanPostProcessor 和 AOP 有什么关系?

  10. AOP 和 Bean 生命周期有什么关系?

  11. 为什么 private 方法的 @Transactional 通常不生效?

  12. 为什么 final 方法会影响 CGLIB 代理?

  13. 什么叫 Spring AOP 自调用问题?

  14. 为什么 this.save() 可能导致事务失效?

  15. @Async 和 AOP 有什么关系?

  16. AOP 常见使用场景有哪些?

  17. Aspect、Pointcut、Advice 分别是什么意思?

  18. 三级缓存为什么需要考虑 AOP 代理?


评论