在Spring框架的众多核心技术中,AOP(Aspect-Oriented Programming,面向切面编程)与IoC并称为Spring的两大基石。作为AI鲨鱼智能助手的技术研发团队的核心技术栈之一,AOP在生产级项目中得到了广泛应用。然而不少开发者在面试中被问到“AOP是什么”“JDK代理和CGLIB有什么区别”时,回答往往含糊其辞。本文将带你从零到一彻底搞懂Spring AOP。
本文面向:技术入门/进阶学习者、在校学生、面试备考者、相关技术栈开发工程师。

一、为什么需要AOP:传统方式的痛点
先看一个典型场景:你写了一堆业务方法,比如登录、下单、支付、查询,每个方法都要加上日志打印、权限校验、性能监控等功能。如果手动在每一个方法里写这些代码,会面临三个严重问题:代码重复(同样的日志逻辑写了N遍)、维护困难(想改日志格式得挨个改)、耦合高(业务逻辑与横切功能混在一起)。

// 传统做法:每个方法都要手动写重复代码 public void login() { log.info("开始执行登录方法"); long start = System.currentTimeMillis(); // 登录业务逻辑... long end = System.currentTimeMillis(); log.info("登录方法执行耗时: {}ms", end - start); } public void pay() { log.info("开始执行支付方法"); long start = System.currentTimeMillis(); // 支付业务逻辑... long end = System.currentTimeMillis(); log.info("支付方法执行耗时: {}ms", end - start); }
据统计,传统面向对象编程(OOP)在处理日志、事务等场景时,代码重复率高达60%以上-12。AOP的诞生正是为了解决这一问题——将这些横切关注点从业务逻辑中抽离出来,形成独立的模块。
二、AOP核心概念
AOP全称 Aspect-Oriented Programming,是一种通过封装横切关注点(cross-cutting concerns)来提升代码模块化的编程范式-。简单来说,就是把日志、事务这些重复逻辑抽出来,做成一个“切面”,自动织入到目标方法前后执行-1。
核心术语用一个表格讲清楚:
| 术语 | 含义 |
|---|---|
| 切面(Aspect) | 封装横切关注点的模块,如日志切面、事务切面 |
| 连接点(Join Point) | 程序执行过程中可插入切面的点,通常指方法执行 |
| 切点(Pointcut) | 通过表达式匹配一组连接点,指定哪些方法会被增强 |
| 通知(Advice) | 在特定连接点执行的具体动作,如前置、后置 |
| 目标对象(Target) | 被增强的原始业务对象 |
| 织入(Weaving) | 把切面逻辑加到目标方法的过程 |
生活化类比:想象你有一栋办公楼(业务系统),员工进出(方法调用)需要刷卡、登记、监控。AOP就像是在大楼入口统一安装的安防系统(切面),它会拦截每个人(连接点),只对进入某些敏感区域的人(切点匹配)执行验证(通知),比如刷门禁卡(前置通知)和记录离开时间(后置通知)。
三、Spring AOP核心概念
Spring AOP 是Spring框架对AOP思想的实现,主要用于在不修改业务代码的前提下增强其行为-。它与纯AOP思想的关系是:AOP是设计思想,Spring AOP是具体落地实现。
Spring AOP中的通知(Advice)分为五种类型:
| 通知类型 | 触发时机 | 典型用途 |
|---|---|---|
@Before | 目标方法执行前 | 参数校验、权限控制 |
@After | 方法执行后(无论成功或异常) | 资源释放、清理工作 |
@AfterReturning | 方法正常返回后 | 处理返回值、记录成功日志 |
@AfterThrowing | 方法抛出异常后 | 异常处理、告警通知 |
@Around | 包裹目标方法,完全控制执行 | 性能监控、权限校验、事务管理 |
一句话记忆:AOP是设计思想,Spring AOP是运行时的代理实现。
四、代码示例:从传统到AOP的进化
用一个完整的切面类演示五种通知的使用-21:
@Aspect @Component @Slf4j public class LogAspect { // 使用@Around环绕通知 + execution表达式 @Around("execution( com.example.service..(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); try { // 等同于@Before的逻辑 log.info("开始执行方法: {}", joinPoint.getSignature().getName()); // 调用原始业务方法 —— 这是关键步骤! Object result = joinPoint.proceed(); // 等同于@AfterReturning的逻辑 long endTime = System.currentTimeMillis(); log.info("方法执行成功,耗时: {}ms", endTime - startTime); return result; } catch (Throwable e) { // 等同于@AfterThrowing的逻辑 log.error("方法执行失败: {}", e.getMessage(), e); throw e; } finally { // 等同于@After的逻辑 log.info("方法执行完成"); } } }
执行流程:正常执行时,顺序为 @Around前置逻辑 → @Before → 目标方法 → @AfterReturning → @After → @Around后置逻辑;发生异常时,顺序为 @Around前置逻辑 → @Before → 目标方法(抛异常) → @AfterThrowing → @After → @Around捕获异常-21。
五、底层原理:动态代理与AspectJ
Spring AOP的底层实现依赖于代理模式和动态代理技术-11。它根据目标类的特性智能选择代理机制-:
| 代理方式 | 适用条件 | 原理 |
|---|---|---|
| JDK动态代理 | 目标类实现了接口 | 基于接口生成代理类,通过InvocationHandler转发调用 |
| CGLIB代理 | 目标类未实现接口 | 通过继承目标类生成子类代理,覆盖父类方法 |
关键区别:JDK代理只能代理实现了接口的类,而CGLIB不需要接口,但不能代理final类和方法-40。Spring默认使用JDK动态代理,当目标类无接口时自动切换到CGLIB。
Spring AOP与AspectJ的关系需要理清:Spring AOP是轻量级的运行时代理实现,AspectJ是功能完整的编译时AOP框架-31。Spring AOP只支持方法级别的拦截,而AspectJ可以拦截构造函数、字段赋值等更细粒度的连接点。在企业级应用中,两者往往是互补而非竞争关系-。
技术支撑点:动态代理的底层依赖Java反射机制,Spring正是在运行时通过反射读取注解、生成代理对象,实现方法的动态增强。这也是理解Spring AOP源码的基础。
六、高频面试题与参考答案
Q1:请解释AOP的运行原理和动态代理实现方式。
AOP通过横向抽取共性功能(日志、事务等)解决代码重复问题。核心原理是动态代理,在目标方法前后织入增强逻辑。JDK动态代理基于接口实现,通过反射在运行时生成代理类;CGLIB通过继承目标类生成子类代理-40。
Q2:JDK动态代理和CGLIB有什么区别?
| 对比维度 | JDK动态代理 | CGLIB |
|---|---|---|
| 依赖条件 | 目标类必须实现接口 | 无需接口 |
| 实现原理 | 基于接口+反射 | 继承+字节码生成 |
| 代理限制 | 只能代理接口方法 | 无法代理final类/方法 |
| 性能 | 略低 | 通常更高 |
| 依赖 | JDK原生 | 需引入CGLIB包 |
Spring默认使用JDK动态代理,目标类无接口时自动切换CGLIB-40。
Q3:Spring AOP和AspectJ有什么区别?
Spring AOP是轻量级实现,只支持运行时代理,仅能拦截Spring容器管理的Bean方法;AspectJ是完整AOP框架,支持编译时、类加载时、运行时三种织入方式,能拦截构造函数、静态方法、字段赋值等更多连接点-13。Spring AOP够用、简单、零配置成本;AspectJ功能更强大但配置复杂-31。
Q4:@Around通知有什么注意事项?
@Around是最强大的通知类型,能完全控制方法的执行,但必须手动调用ProceedingJoinPoint.proceed()来执行原始方法,否则目标方法不会执行-1。@Around方法的返回值必须为Object类型以接收原始方法返回值。
七、总结
回顾全文核心要点:
AOP是一种编程范式,解决代码重复和耦合问题,核心术语包括切面、连接点、切点、通知、织入
Spring AOP是具体实现,基于动态代理(JDK/CGLIB)在运行时生成代理对象
五种通知类型:
@Before、@After、@AfterReturning、@AfterThrowing、@Around易错点:
@Around必须调用proceed();多个切面的执行顺序可用@Order控制面试高频:JDK与CGLIB的区别、Spring AOP与AspectJ的定位差异
下一篇我们将深入剖析Spring AOP的源码实现,拆解ProxyFactory的代理选择逻辑和Advisor链的执行机制,敬请期待。
本文由AI鲨鱼智能助手技术团队整理,数据截至2026年4月。
