首页 研发技术文章正文

【万一AI助手】2026-04-10 Spring AOP从入门到面试,一篇文章搞定

研发技术 2026年04月29日 05:36 1 小编

更新时间:2026年04月10日 | 预计阅读时间:15分钟

本文导读万一AI助手注意到,AOP(Aspect-Oriented Programming,面向切面编程)作为Spring框架的核心特性之一,与IoC共同构成了Spring两大基石,是每个Java开发者绕不开的必学知识点。然而许多开发者在日常使用中仍然存在“只会用注解、不懂底层原理、面试被问倒”的痛点——本文将从痛点出发,深入浅出地讲解AOP的核心概念、实现机制、代码实战与面试高频题,帮助你建立从“会用”到“懂原理”的完整知识链路。


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

先来看一个典型场景。假设你有一个业务服务类,包含登录、下单、支付等多个方法:

java
复制
下载
public class UserService {
    public void login() {
        // 业务逻辑
    }
    
    public void order() {
        // 业务逻辑
    }
    
    public void pay() {
        // 业务逻辑
    }
}

现在你需要在每个方法中加入:日志打印、权限校验、性能监控、事务控制。如果手动在每个方法里编写这些重复代码:

java
复制
下载
public void login() {
    // 日志记录
    // 权限校验
    long start = System.currentTimeMillis();
    // 业务逻辑
    long end = System.currentTimeMillis();
    // 事务控制
}

这段代码存在明显缺陷:

问题说明
代码冗余相同逻辑在多个方法中重复出现
耦合度高业务逻辑与非业务逻辑混在一起
维护困难修改某个横切逻辑需要改动所有方法
扩展性差新增功能需要在各处分别添加

AOP正是为了解决这类“横切关注点”问题而诞生的-2


二、核心概念讲解:AOP(面向切面编程)

2.1 标准定义

AOP,全称 Aspect-Oriented Programming(面向切面编程),是Spring框架两大核心思想之一(另一个是IoC)。它允许开发者在不修改原有业务代码的前提下,对方法进行增强,统一处理日志、事务、权限、监控等横切逻辑-2

2.2 拆解关键词

  • 切面(Aspect) :将横切逻辑封装成一个模块,如日志切面、事务切面。

  • 横切关注点:那些跨越多个模块的公共功能,与核心业务逻辑无关但又必须执行。

2.3 生活化类比

把业务系统想象成一座大楼的垂直电梯井(纵向的OOP结构)。大楼里的每个房间(业务模块)都需要完成几件事:供电、供水、消防检查。如果在每个房间里都独立布设电线和管道,不仅浪费还会导致火灾隐患。AOP的做法就是设计一个统一的管理系统——在大楼中间挖一个横向的管道井(切面),统一处理所有房间的供电、供水、消防需求。

2.4 核心价值

  • 解耦:业务代码不再混杂日志、事务等逻辑

  • 可维护:切面集中管理,改一处全应用生效

  • 非侵入:无需修改原有业务代码-11


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

3.1 通知(Advice)—— “何时做”

通知定义了切面逻辑在目标方法的哪个时间点执行。Spring AOP支持五种通知类型:

通知类型注解执行时机典型场景
前置通知@Before目标方法执行前参数校验、权限控制
后置通知@After目标方法执行后(无论是否异常)资源清理
返回通知@AfterReturning目标方法正常返回后记录返回值
异常通知@AfterThrowing目标方法抛出异常后异常上报、回滚事务
环绕通知@Around包裹目标方法,可控制执行流程性能监控、事务管理(最强大)

@Around环绕通知是功能最全的,需要手动调用proceed()来执行目标方法-2

3.2 切点(Pointcut)—— “对谁做”

切点通过表达式来匹配哪些连接点(即哪些方法)需要被增强。最常用的execution表达式格式为:

text
复制
下载
execution(修饰符? 返回值类型 包名.类名.方法名(参数) 异常?)

示例

  • execution( com.example.service..(..)):匹配service包下所有类的所有方法

  • execution(public com.example.service.UserService.save(..)):匹配UserService中以save开头的public方法

3.3 概念关系总结

概念英文作用
连接点Join Point可以被增强的方法(潜在目标)
切点Pointcut指定真正要增强哪些连接点(筛选规则)
通知Advice增强逻辑的具体动作
切面Aspect切点 + 通知的组合模块

一句话记忆:切点决定对谁做,通知决定何时做,切面把两者打包成一个模块-2


四、代码示例演示:一个完整的AOP实战

4.1 添加依赖

在Spring Boot项目中,AOP依赖已经包含在spring-boot-starter-aop中:

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

4.2 业务服务类

java
复制
下载
@Service
public class UserService {
    public void login(String username, String password) {
        System.out.println("执行登录业务逻辑:" + username);
    }
    
    public String getUserInfo(Long userId) {
        System.out.println("查询用户信息:" + userId);
        return "用户" + userId;
    }
}

4.3 定义切面类

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

@Aspect          // 1. 标记这是一个切面类
@Component       // 2. 交给Spring管理
public class LogAspect {
    
    // 3. 定义切点:匹配UserService中的所有方法
    @Pointcut("execution( com.example.service.UserService.(..))")
    public void servicePointcut() {}
    
    // 前置通知:方法执行前记录
    @Before("servicePointcut()")
    public void logBefore() {
        System.out.println("[前置通知] 方法即将执行");
    }
    
    // 环绕通知:统计方法执行耗时
    @Around("servicePointcut()")
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long begin = System.currentTimeMillis();
        System.out.println("[环绕通知] 开始计时");
        
        Object result = joinPoint.proceed();  // 调用原始业务方法
        
        long end = System.currentTimeMillis();
        System.out.println("[环绕通知] 执行耗时:" + (end - begin) + "ms");
        return result;
    }
    
    // 返回通知:记录返回值
    @AfterReturning(pointcut = "servicePointcut()", returning = "retVal")
    public void logReturn(Object retVal) {
        System.out.println("[返回通知] 方法返回:" + retVal);
    }
}

4.4 运行结果

当调用userService.getUserInfo(1L)时,控制台输出:

text
复制
下载
[环绕通知] 开始计时
[前置通知] 方法即将执行
查询用户信息:1
[返回通知] 方法返回:用户1
[环绕通知] 执行耗时:5ms

4.5 关键注解说明

注解作用
@Aspect标记该类为一个切面类
@Component将切面类交给Spring容器管理
@Pointcut定义切入点表达式
@Before / @After / @Around定义通知及其执行时机
@Around中的proceed()必须调用,否则原始方法不会执行

切面类必须放在启动类所在包及其子包下,否则需要在@SpringBootApplication上手动指定扫描包-2


五、底层原理:Spring AOP如何实现?

Spring AOP的实现本质上依赖于代理模式——通过引入代理对象作为目标对象的中间层,在调用目标方法前后插入增强逻辑-57

5.1 两种动态代理方式

Spring AOP底层通过两种动态代理技术实现:

维度JDK动态代理CGLIB动态代理
实现原理基于接口,通过反射生成代理类基于继承,通过ASM字节码生成子类
依赖条件目标类必须实现至少一个接口无需接口,但不能是final类/方法
代理创建较快较慢(需生成字节码)
方法调用反射调用,性能略低直接调用,性能更高
依赖库Java原生支持,无需第三方需要cglib(Spring Core已内置)

关键说明:JDK动态代理通过java.lang.reflect.ProxyInvocationHandler实现,代理类实现目标接口,将方法调用转发给处理器-38。CGLIB则通过Enhancer生成目标类的子类作为代理,重写非final方法-37

5.2 Spring的代理选择策略

Spring AOP会根据目标类是否实现接口自动选择代理方式-

  • 如果目标类实现了接口 → 默认使用 JDK动态代理

  • 如果目标类没有实现接口 → 自动切换到 CGLIB代理

如需强制使用CGLIB,可在application.properties中配置:

properties
复制
下载
spring.aop.proxy-target-class=true

性能提示:在JDK 8及更高版本中,JDK动态代理与CGLIB的性能差距已显著缩小-37

5.3 常见陷阱:自调用问题

当一个Bean内部的方法(如this.methodB())调用另一个内部方法时,切面不会生效——因为调用绕过了代理对象,直接调用了原始对象。解决方案是通过@Autowired注入自身代理对象,使用self.methodB()进行调用-67


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

Q1:什么是AOP?和OOP有什么区别?

参考答案
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,通过横向抽取共性功能(如日志、事务、权限)来解决代码重复问题。OOP关注纵向的继承与封装,以对象为核心;AOP则关注横向的切面,补充OOP在处理横切关注点时的不足-17

踩分点:提到横切关注点、与OOP的对比、解决的问题。

Q2:Spring AOP是怎么实现的?JDK动态代理和CGLIB有什么区别?

参考答案
Spring AOP基于动态代理实现,在运行时为目标对象生成代理对象,在方法调用前后织入增强逻辑。

JDK动态代理 vs CGLIB

  • JDK:基于接口,通过反射实现,目标类必须实现接口,Java原生支持

  • CGLIB:基于继承,通过字节码生成子类,无需接口,但不能代理final类/方法

Spring默认优先使用JDK代理,若无接口则自动切换为CGLIB-17-18

踩分点:动态代理、两种方式的原理差异、Spring的自动选择策略。

Q3:AOP有哪些通知类型?分别说明执行时机。

参考答案

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

其中@Around功能最强,需要手动调用proceed()执行目标方法-2-58

Q4:什么是切点表达式?举个例子。

参考答案
切点表达式用于匹配哪些连接点(方法)需要被增强。最常用的是execution表达式。

示例:@Pointcut("execution( com.example.service..(..))")

该表达式匹配com.example.service包下所有类的所有方法。格式为:返回值类型、包名、类名、方法名、参数-2

Q5:Spring AOP和AspectJ有什么关系?

参考答案

  • Spring AOP:运行时通过动态代理实现,仅支持方法级别的连接点,轻量级,适合大多数企业应用

  • AspectJ:编译时或类加载时织入,支持字段、构造器等更丰富的连接点,功能更强,但需要额外配置

Spring AOP借鉴了AspectJ的注解风格(如@Aspect@Before),但底层实现不同-58

踩分点:织入时机差异、支持范围差异、注解风格的关系。


七、结尾总结

核心知识点回顾

知识点要点
AOP定义面向切面编程,将横切关注点与业务逻辑分离
核心概念切面、连接点、切点、通知、织入
通知类型@Before、@After、@AfterReturning、@AfterThrowing、@Around
实现原理基于动态代理(JDK代理 + CGLIB代理)
面试重点AOP定义、两种代理方式的区别、通知类型、切点表达式

重点提示

  1. 注意切点表达式的写法——写得太宽会影响性能,尽量精确匹配

  2. 注意自调用问题——同一个Bean内部的方法调用会绕过代理,切面不生效

  3. 注意final限制——CGLIB无法代理final类和final方法

进阶预告

本文重点讲解了AOP的核心概念与实现机制。下一篇文章我们将深入探讨:

  • Spring AOP的源码级解析(通知执行链路、责任链模式)

  • 如何自定义注解驱动的AOP

  • AOP在微服务监控和分布式追踪中的实际应用

学习建议:看完本文后,建议亲自搭建一个Spring Boot项目,写一个日志切面跑一遍,代码跑通的那一刻,才是真正理解AOP的开始。

上海羊羽卓进出口贸易有限公司 备案号:沪ICP备2024077106号