演员突出AI助手解析Spring AOP核心技术(2026.04.10)

小编头像

小编

管理员

发布于:2026年05月10日

3 阅读 · 0 评论

【演员突出AI助手】 今天我们来聊一聊 Spring AOP—— 一个让无数 Java 开发者又爱又恨的核心知识点。作为 演员突出AI助手,我将从痛点切入,带你系统掌握 AOP 的核心概念、底层原理和高频面试要点。


一、为什么需要 AOP?从痛点说起

假设你正在开发一个电商系统,订单、支付、用户等模块都需要记录操作日志。传统做法是在每个业务方法里手动插入日志代码:

java
复制
下载
public void createOrder(Order order) {

log.info("开始创建订单,参数: {}", order); // 核心业务逻辑 orderService.save(order); log.info("订单创建成功"); } public void cancelOrder(Long id) { log.info("开始取消订单,订单ID: {}", id); // 核心业务逻辑 orderService.cancel(id); log.info("订单取消成功"); }

这种方式存在三个致命问题:

  1. 代码重复:日志、事务、权限校验等逻辑在成百上千个方法中反复出现

  2. 耦合度高:横切逻辑与核心业务代码纠缠在一起,任何一个修改都可能波及业务

  3. 维护困难:新增一个需要日志的功能,就要到处找地方插入代码,极易遗漏

为了解决这些问题,AOP(Aspect Oriented Programming,面向切面编程)应运而生-1。它的核心思想是:把那些影响多个类的公共行为封装成独立模块(切面),再通过动态代理技术“织入”到业务方法中,从而彻底解放业务代码-38

二、核心概念:理解 AOP 的五大关键词

AOP 有五组核心概念,理解它们就等于掌握了 AOP 的“行话”-1-38

1. 连接点(JoinPoint)

定义:程序中可以被 AOP 拦截的“点位”,在 Spring AOP 中特指方法的执行-1

通俗理解:连接点就像商场里所有可能被安检的入口——每个入口理论上都可以拦截,但最终是否拦截由规则决定。

2. 切点(Pointcut)

定义:通过表达式定义的一组匹配规则,用来筛选哪些连接点需要被增强-1

通俗理解:切点就像安检规则——“只检查背双肩包的顾客”。它从所有可能被拦截的点位中,筛选出真正需要增强的那部分。

3. 通知(Advice)

定义:在切点匹配到的连接点上执行的增强逻辑。Spring AOP 提供五种通知类型-1

通知类型注解执行时机
前置通知@Before目标方法执行前
后置通知@After目标方法执行后(无论是否异常)
返回通知@AfterReturning目标方法正常返回后
异常通知@AfterThrowing目标方法抛出异常时
环绕通知@Around完全包裹目标方法,可控制执行流程

4. 切面(Aspect)

定义:切点 + 通知的封装体,用 @Aspect 注解标识-1

通俗理解:切面 = 规则(切点)+ 动作(通知)。就像安检流程:规则是“检查谁”,动作是“怎么检查”。

5. 织入(Weaving)

定义:把切面逻辑应用到目标对象,生成代理对象的过程-1

三、代理实现方式:JDK 动态代理 vs CGLIB

理解了概念之后,一个更关键的问题是:Spring AOP 在底层到底是怎么实现的?

答案是——动态代理。Spring AOP 本质上是代理模式的一种应用,通过创建目标对象的代理对象,在代理对象上织入增强逻辑-21。Spring 提供了两种动态代理实现方式-12-11

JDK 动态代理

  • 实现原理:基于 Java 反射机制,要求目标类必须实现至少一个接口。通过 Proxy.newProxyInstance 创建实现了接口的代理对象,方法调用时回调 InvocationHandler.invoke 方法-22-11

  • 优点:性能较好,无需引入第三方库

  • 缺点:必须实现接口,灵活性受限

  • 适用场景:目标类有接口实现,且对性能有一定要求

CGLIB 动态代理

  • 实现原理:通过字节码技术生成目标类的子类,重写目标方法并在方法调用前后插入切面逻辑-11

  • 优点:不要求实现接口,可以代理普通类

  • 缺点:性能略逊于 JDK(因为需要生成子类),无法代理 final 类或 final 方法

  • 适用场景:目标类没有实现接口,或需要代理类级别的方法

Spring 如何选择?

  • Spring Framework(非 Boot) :默认策略——目标类有接口就选 JDK,没有就选 CGLIB-

  • Spring Boot 2.x 及以上:默认使用 CGLIB 代理(因为 spring-boot-starter-aop 默认 proxyTargetClass=true

如果想强制指定代理方式,可以通过 @EnableAspectJAutoProxy(proxyTargetClass=true) 配置-22

四、实战示例:一个完整的日志切面

下面通过一个完整的代码示例,让你直观感受 AOP 的魅力。

java
复制
下载
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect          // 标记这是一个切面类
@Component       // 交给 Spring 容器管理
public class LoggingAspect {

    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);

    // 定义切点:匹配 service 包下所有类的所有方法
    @Pointcut("execution( com.example.service...(..))")
    public void servicePointcut() {}

    // 环绕通知:计算方法执行耗时
    @Around("servicePointcut()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().toShortString();
        
        log.info("开始执行方法: {}", methodName);
        
        try {
            Object result = joinPoint.proceed();   // 关键步骤:执行原方法
            long duration = System.currentTimeMillis() - start;
            log.info("方法 {} 执行完成,耗时 {}ms,返回结果: {}", methodName, duration, result);
            return result;
        } catch (Throwable e) {
            long duration = System.currentTimeMillis() - start;
            log.error("方法 {} 执行失败,耗时 {}ms,异常: {}", methodName, duration, e.getMessage(), e);
            throw e;   // 重新抛出异常,不吞掉
        }
    }

    // 前置通知:仅在方法执行前做简单记录
    @Before("execution( com.example.controller...(..))")
    public void logBefore(JoinPoint joinPoint) {
        log.info("即将调用方法: {},参数: {}", joinPoint.getSignature().getName(), joinPoint.getArgs());
    }
}

关键步骤说明

  1. @Aspect + @Component 让 Spring 识别并管理这个切面类

  2. @Pointcut 定义可复用的匹配规则,避免到处重复写表达式

  3. joinPoint.proceed() 是环绕通知的核心——必须手动调用,否则原业务方法永远不会执行-1

  4. 代理对象调用方法时,自动触发通知逻辑,原业务代码完全无感知

五、底层原理:代理模式如何支撑 AOP?

Spring AOP 的底层实现本质上依赖于代理模式。代理模式通过引入代理对象作为目标对象的中间层,实现对目标对象访问的控制与增强-21

两种代理的底层机制

JDK 动态代理CGLIB 动态代理
核心技术反射(java.lang.reflect.Proxy + InvocationHandler字节码技术(ASM 库)
代理关系代理类实现目标接口代理类继承目标类
执行流程调用代理方法 → invoke() → 反射调用原方法 + 增强逻辑调用子类重写方法 → 增强逻辑 → super.原方法()
关键限制目标类必须有接口目标类不能是 final

代理对象的创建流程

  1. Spring 容器初始化时,识别被 @Aspect 标记的切面类

  2. 通过 @EnableAspectJAutoProxy 开启 AOP 自动代理功能-11

  3. Spring 根据目标类的特征,选择 JDK 或 CGLIB 创建代理工厂

  4. 代理工厂创建代理对象,替代原 Bean 注册到容器中

  5. 客户端调用方法时,实际执行的是代理对象的增强方法-11

六、高频面试题与参考答案

面试题 1:Spring AOP 的实现原理是什么?

参考答案

Spring AOP 基于动态代理实现。当目标类实现接口时,使用 JDK 动态代理(基于反射,通过 Proxy 和 InvocationHandler 创建接口代理);当目标类无接口或强制指定时,使用 CGLIB 动态代理(通过字节码技术生成目标类的子类代理)。Spring 在容器初始化时,为符合条件的 Bean 创建代理对象,将切面逻辑织入代理方法中,从而实现对目标方法的增强-39

面试题 2:JDK 动态代理和 CGLIB 有什么区别?

参考答案

对比维度JDK 动态代理CGLIB 动态代理
实现方式基于反射,实现接口基于字节码,继承目标类
接口要求目标类必须实现接口无接口要求
代理限制只能代理接口方法无法代理 final 类/方法
性能代理生成快,执行稍慢代理生成慢,执行更快
依赖JDK 原生需引入 cglib 库

Spring Boot 2.x 后默认使用 CGLIB-39

面试题 3:@Around 通知和其他通知的区别是什么?

参考答案

@Around 是功能最强的通知类型,可以完全控制目标方法的执行流程。区别如下:

  • @Before/@After:无法获取/修改方法返回值,无法阻止方法执行

  • @AfterReturning:只能获取返回值,无法修改

  • @AfterThrowing:只能处理异常

  • @Around:可控制是否执行目标方法、修改参数、修改返回值、统一异常处理-28

注意:@Around 必须手动调用 proceed() 才能执行原方法,且返回值类型必须为 Object-1

面试题 4:Spring AOP 和 AspectJ 有什么区别?

参考答案

  • Spring AOP:Spring 自研实现,基于动态代理,仅支持方法级别的连接点,在运行时织入,更加轻量级

  • AspectJ:独立的 AOP 框架,功能更强大,支持字段、构造函数等更多连接点类型,可在编译时、类加载时、运行时织入

Spring AOP 借用了 AspectJ 的注解语法(如 @Aspect、@Before),但底层实现完全不同-

面试题 5:AOP 不生效的常见原因有哪些?

参考答案

  1. 目标类没有被 Spring 容器管理(未加 @Service/@Component 等注解)

  2. 切面类没有被 Spring 扫描(未加 @Component 或在启动类包外)

  3. 目标方法是 private 或 final 的(CGLIB 无法代理)

  4. 同一个类内部方法调用(this.method()),绕过了代理对象

  5. 切点表达式写错了,没有匹配到任何方法-28

七、总结

本文围绕 Spring AOP 的核心知识点展开,总结如下:

学习维度核心要点
核心概念切面、连接点、切点、通知、织入——五词记住 AOP
实现方式JDK 动态代理(基于反射+接口)vs CGLIB(基于字节码+继承)
五种通知@Before、@After、@AfterReturning、@AfterThrowing、@Around
底层原理代理模式 + 反射/字节码,运行时生成代理对象并织入增强逻辑
面试重点实现原理、两种代理区别、@Around 特殊之处、不生效原因

学习建议

  • 先动手跑通一个简单的 @Around 日志切面,感受 AOP 的“无侵入”特性

  • 理解代理模式是 AOP 的底层思想,JDK 和 CGLIB 只是具体实现

  • 注意面试考点:AOP 不生效的场景是高频坑点,面试官最爱追问


下篇预告:深入剖析 AOP 代理创建源码,带你从 @EnableAspectJAutoProxy 一路追到 JdkDynamicAopProxyCglibAopProxy 的底层实现。敬请期待!

标签:

相关阅读