标题:ai速记助手带你5分钟搞懂Spring AOP核心原理(20260409)

小编头像

小编

管理员

发布于:2026年05月09日

7 阅读 · 0 评论

面对混乱的代码逻辑、低下的开发效率,你是否曾陷入深深的无力感?在复杂业务交织的项目中,总有些需求(如日志、权限)遍布各处,写一次重复一次,不仅毫无技术价值,还极易引入Bug。本文将借助

ai速记助手 的与梳理能力,系统性讲解 AOP 面向切面编程,助你彻底扫清知识盲区。

一、痛点切入:为什么需要AOP?

先看一段传统实现的代码:

java
复制
下载
public class UserService {

public void register() { // 重复代码:记录开始时间 + 权限校验 System.out.println("开始时间:" + System.currentTimeMillis()); if (!hasPermission()) throw new SecurityException(); // 核心业务逻辑 System.out.println("执行注册业务逻辑"); // 重复代码:记录结束时间 + 日志 System.out.println("结束时间:" + System.currentTimeMillis()); System.out.println("用户注册完成"); } }

痛点分析:

  • 代码冗余:日志、权限、性能统计等代码在多个方法中重复出现

  • 耦合度高:核心业务与非核心关注点混在一起,修改一处可能影响多处

  • 扩展性差:新增横切功能(如缓存)需要逐一修改所有目标方法

  • 维护困难:横切逻辑散落各处,修改成本高、易遗漏

AOP(Aspect Oriented Programming,面向切面编程)正是为了解决这些问题而诞生的编程范式。

二、核心概念讲解:切面(Aspect)

标准定义:AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,通过将横切关注点(如日志记录、事务管理、权限验证等)封装成切面,在不修改原有业务代码的基础上,动态地织入到目标对象的方法执行前后-2

生活化类比

  • 把餐厅比作系统,厨师炒菜是核心业务(如用户注册)

  • 餐前摆盘、餐后洗碗是横切关注点(如日志记录)

  • 切面就是把这些横切工作打包成一个独立模块——洗碗阿姨只负责洗碗,不关心厨师在炒什么菜

核心价值:分离核心关注点与横切关注点,降低耦合、提升复用性-2

三、关联概念讲解:通知(Advice)与切点(Pointcut)

通知(Advice)

切面的具体执行逻辑。Spring AOP 支持 五种通知类型-30

通知类型执行时机典型应用
前置通知(@Before)目标方法执行前权限校验、参数验证
后置通知(@After)目标方法执行后(无论是否异常)资源清理
返回通知(@AfterReturning)目标方法正常返回后日志记录、返回值处理
异常通知(@AfterThrowing)目标方法抛出异常后异常捕获与处理
环绕通知(@Around)包裹目标方法,可控制执行过程性能监控、事务控制

切点(Pointcut)

定义通知应用在哪些方法上的筛选规则-13

java
复制
下载
// 切点表达式示例:拦截com.example.service包下所有类的所有方法
@Pointcut("execution( com.example.service..(..))")
public void serviceLayer() {}

四、概念关系与区别总结

概念一句话理解
连接点(JoinPoint)程序执行中可以被拦截的时机,Spring中主要指方法调用-13
切点(Pointcut)从众多连接点中筛选出需要增强的规则
通知(Advice)拦截到连接点后要执行的具体操作
切面(Aspect)切点 + 通知,描述在何时、何地执行什么操作-13
织入(Weaving)将切面应用到目标对象并创建代理对象的过程-13

💡 记忆口诀:切点定位置,通知定动作,切面二合一,织入生代理。

五、代码示例:用AOP优雅实现日志记录

Spring Boot 整合 AOP 完整示例-36

步骤1:引入依赖(pom.xml)

xml
复制
下载
运行
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

步骤2:定义切面类

java
复制
下载
@Aspect
@Component
public class LoggingAspect {
    
    // 切点:拦截service包下所有类的所有方法
    @Pointcut("execution( com.example.service..(..))")
    public void serviceLayer() {}
    
    // 环绕通知:统计方法执行耗时
    @Around("serviceLayer()")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        // 获取方法签名
        String methodName = joinPoint.getSignature().toShortString();
        System.out.println("〖前置〗方法" + methodName + "开始执行");
        
        // 执行目标方法(核心!)
        Object result = joinPoint.proceed();
        
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("〖后置〗方法" + methodName + "执行耗时:" + elapsed + "ms");
        return result;
    }
}

步骤3:业务代码(完全无侵入)

java
复制
下载
@Service
public class UserService {
    public void register() {
        // 只写核心业务逻辑,日志自动切入
        System.out.println("执行注册业务逻辑");
    }
}

执行效果对比

  • 改造前:每个方法都需要手动写开始时间、结束时间、日志输出

  • 改造后:业务代码保持纯净,日志统一由切面管理

六、底层原理:动态代理

Spring AOP 底层基于 动态代理 技术,核心是用代理对象包装原始Bean,让方法执行过程被增强-40

两种代理方式对比

对比维度JDK动态代理CGLIB代理
代理方式接口代理子类代理(继承)
是否依赖接口必须实现接口无需接口
核心原理Proxy + InvocationHandlerASM字节码生成子类
final类/方法不支持不支持(无法继承/重写)
创建成本较高
执行性能略低更高
Spring默认选择有接口时使用无接口时使用

核心执行流程-20-40

text
复制
下载
客户端调用 → 代理对象拦截 → 获取通知链 → 按顺序执行通知 → 调用目标方法 → 继续通知链 → 返回结果

💡 关键点:Bean在初始化阶段被真实对象创建,但在注入到容器时被替换为代理对象,因此内部调用(this.method())会绕过代理,导致AOP失效-40

七、典型应用场景

场景说明示例
日志记录方法调用前后记录日志记录入参、出参、执行时间-30
权限控制方法执行前校验权限检查用户是否有权限访问接口-30
事务管理声明式事务Spring @Transactional注解底层基于AOP
性能监控统计方法耗时找出慢查询、慢接口
缓存管理方法执行前检查缓存命中则返回,未命中则执行方法-2

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

⭐ Q1:什么是AOP?

参考答案:AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,核心思想是将与核心业务无关、但多个模块共有的逻辑(如日志、事务、权限)抽取为“切面” ,在不修改原有业务代码的前提下,通过动态代理在方法执行前后织入增强逻辑,实现代码解耦-57

⭐ Q2:AOP的核心术语有哪些?

参考答案(6个核心术语):

  • 切面(Aspect) :抽取的公共逻辑模块,包含通知和切入点

  • 通知(Advice) :切面的具体执行逻辑(@Before、@After、@Around等)

  • 切点(Pointcut) :定义切面作用于哪些方法的规则

  • 连接点(JoinPoint) :程序执行过程中可插入切面的时机

  • 织入(Weaving) :将切面动态融入目标对象,生成代理对象的过程

  • 目标对象(Target) :被切面作用的原始业务对象-57

⭐ Q3:Spring AOP 和 AspectJ 的区别?

对比维度Spring AOPAspectJ
织入时机运行时(动态代理)编译期/类加载期
功能范围仅方法级拦截字段、构造器、静态方法等全面拦截
复杂度简单,与Spring集成度高功能强大,配置相对复杂
最新动态Spring 6.3+已支持虚拟线程AspectJ 2.0支持GraalVM Native Image

标准回答:Spring AOP是运行时基于动态代理实现的,与Spring生态集成度高,足以满足业务开发需求;AspectJ功能更全面,支持编译时织入,适合框架层面或需要拦截构造器、静态方法的场景-47

⭐ Q4:为什么 @Transactional 有时会失效?

参考答案(3个最常见原因):

  1. 方法不是 public(事务只作用于 public 方法)

  2. 同一类内部调用this.method() 没有经过代理对象)

  3. final 方法无法被代理(CGLIB通过继承实现,final方法无法重写)-51

⭐ Q5:@Around 和其他通知的区别是什么?

参考答案@Around最强大的通知类型,通过 ProceedingJoinPoint.proceed() 手动控制目标方法的执行,可以实现:

  • 控制目标方法是否执行(不调用 proceed 则跳过)

  • 修改入参(通过 proceed(args) 传入新参数)

  • 修改返回值

  • 捕获异常并自定义处理

@Before / @After 仅能在方法前后附加逻辑,无法控制方法是否执行-57

九、结尾总结

核心知识回顾:

为什么需要AOP:解决代码冗余、耦合度高、扩展性差的痛点
核心概念:切面、通知、切点、连接点、织入
底层原理:JDK动态代理(接口代理)+ CGLIB动态代理(子类代理)
典型场景:日志记录、权限校验、事务管理、性能监控
注意事项:代理模式下的内部调用失效、final方法不可代理

📌 进阶预告:下一篇将深入探讨 AOP 与 IOC 的协作机制,以及如何在微服务架构中利用 AOP 实现全链路日志追踪。欢迎持续关注!

标签:

相关阅读