一文掌握Spring AOP:从入门到底层原理

前言

AOP 是 Spring 核心特性之一,也是面试高频考点、日常开发必备技能。很多同学对 AOP 的理解停留在「加个注解打印日志」,但不懂底层原理,就无法真正驾驭它

这篇文章尽量用为大家讲清,并结合 Bean 生命周期、动态代理,把 Spring AOP 的完整流程、底层原理描述清楚

一、什么是AOP?为什么要用它

1.通俗理解 AOP

AOP = Aspect Oriented Programming(面向切面编程) ,它是OOP(面向对象编程)的补充

  • OOP:以「对象」为核心,关注纵向的业务逻辑(用户登录、订单创建)
  • AOP:以「切面」为核心,关注横向的通用逻辑(日志打印、权限校验、事务管理)

举个生活例子:你要给项目中 100 个 Service 方法都加上方法执行耗时统计

  • OOP 做法:每个方法手动写开始 / 结束时间 → 代码冗余、难以维护
  • AOP 做法:写一个「耗时统计切面」,告诉 Spring:所有 Service 方法执行前后,自动执行统计逻辑 → 无侵入、解耦、易维护

2.AOP核心作用

一句话来描述:在不修改原有业务代码的前提下,给方法统一增强功能

典型场景:日志记录、权限校验、事务管理(Spring 事务底层就是 AOP)、接口限流、监控、异常统一处理

二、AOP相关概念

1.专业术语

要记住六个重要概念,就以给方法打印执行日志举例:

术语 英文 通俗解释
切面 Aspect 封装通用增强逻辑的类(日志、权限类)
连接点 JoinPoint 可以被增强的方法(所有业务方法)
切点 PointCut 真正要增强的目标方法(匹配规则,如所有 Service 方法)
通知 Advice 增强的具体代码逻辑(方法前 / 后 / 异常打印日志)
目标对象 Target 被增强的原始 Bean(UserService、OrderService)
代理对象 Proxy Spring 生成的、包含增强逻辑的新对象(真正执行的对象)

重点:切面=切点+通知、执行流程=代理对象调用目标对象+自动

2.通知的分类

在面向切面编程(AOP)中,通知(Advice)是切面(Aspect)的核心组成部分,用于定义在目标方法的特定执行点插入的横切逻辑。根据执行时机的不同,通知可以分为以下几类:

(1)前置通知(Before Advice)

在目标方法执行前触发,通常用于权限校验、日志记录等场景。例如:

java 复制代码
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice() {
    System.out.println("前置通知:方法执行前");
}

(2)后置通知(After Advice)

无论目标方法是否正常完成,都会在方法执行后触发。适用于资源清理等操作:

java 复制代码
@After("execution(* com.example.service.*.*(..))")
public void afterAdvice() {
    System.out.println("后置通知:方法执行结束");
}

(3)返回通知(After Returning Advice)

仅在目标方法成功执行后触发,可访问返回值。例如记录结果:

java 复制代码
@AfterReturning(pointcut="execution(* com.example.service.*.*(..))", returning="result")
public void afterReturningAdvice(Object result) {
    System.out.println("返回通知:结果=" + result);
}

(4)异常通知(After Throwing Advice)

当目标方法抛出异常时触发,用于异常处理:

java 复制代码
@AfterThrowing(pointcut="execution(* com.example.service.*.*(..))", throwing="ex")
public void afterThrowingAdvice(Exception ex) {
    System.out.println("异常通知:" + ex.getMessage());
}

(5)环绕通知(Around Advice)

最强大的通知类型,可完全控制目标方法的执行流程。需手动调用ProceedingJoinPoint.proceed()

java 复制代码
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕通知:方法执行前");
    Object result = pjp.proceed();
    System.out.println("环绕通知:方法执行后");
    return result;
}

(6)通知的执行顺序

当多个通知作用于同一连接点时,执行顺序通常为:

  1. 环绕通知的前半部分
  2. 前置通知
  3. 目标方法
  4. 返回通知或异常通知
  5. 后置通知
  6. 环绕通知的后半部分

三、Spring AOP最简入门案例

这儿案例可以先体会一下AOP的效果,之后再细讲原理

1.依赖引入

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.业务Bean(目标对象)

java 复制代码
@Service
public class UserService {
    // 连接点 + 切点:要被增强的方法
    public void addUser(String username) {
        System.out.println("执行核心业务:添加用户 -> " + username);
    }
}

3.切面类

java 复制代码
@Aspect // 标记这是一个切面
@Component // 交给 Spring 管理
public class LogAspect {

    // 切点:匹配 UserService 下的所有方法
    @Pointcut("execution(* com.example.service.UserService.*(..))")
    public void pointCut() {}

    // 前置通知:目标方法执行前执行
    @Before("pointCut()")
    public void before() {
        System.out.println("【前置通知】方法开始执行...");
    }

    // 后置通知:目标方法执行后执行
    @After("pointCut()")
    public void after() {
        System.out.println("【后置通知】方法执行完毕");
    }
}

4.测试

java 复制代码
@SpringBootTest
public class AopTest {
    @Autowired
    private UserService userService;

    @Test
    public void test() {
        userService.addUser("张三");
    }
}

输出结果:

java 复制代码
【前置通知】方法开始执行...
执行核心业务:添加用户 -> 张三
【后置通知】方法执行完毕

关键点:我们没有修改UserService的代码,却给他增加了日志功能-----这就是AOP的魅力

四、核心底层:Spring AOP本质=动态代理

1.一句话总结

Spring AOP 底层是动态代理,它不修改原始 Bean 代码,而是生成一个「代理对象」替代原始对象执行。

调用链:调用者 → 代理对象 → 执行通知逻辑 → 执行目标对象方法 → 执行通知逻辑

2.Spring用了两种动态代理

Spring框架会根据目标对象的类型自动选择最合适的代理模式,具体选择逻辑如下:

(1)当目标对象实现了至少一个接口时:默认使用JDK动态代理

(2)当目标对象没有实现任何接口时:使用CGLIB代理

代理方式 适用场景 底层实现
JDK 动态代理 目标类实现了接口 JDK 原生 API
CGLIB 代理 目标类没有实现接口 继承目标类生成子类
  • JDK 代理:基于接口,必须有接口
  • CGLIB 代理:基于继承,生成子类作为代理

3.结合上面的案例来理解代理对象

原始 Bean:UserService(只有核心业务)

代理对象:UserServiceProxy(核心业务 + 日志 / 权限 / 事务增强,注意这个是Spring自动生成的,因此代码里面没有显示)

你在代码中 @Autowired 注入的,不是原始 Bean,而是代理对象

五、Spring AOP完整执行流程

阶段 1:Spring 容器启动,扫描并创建原始 Bean

  1. Spring 扫描 @Service/@Component,创建原始 Bean 实例(UserService)

  2. 原始 Bean 只是普通对象,没有任何增强功能

阶段 2:AOP 核心步骤 ------ 自动生成代理对象(关键)

Spring 在 Bean 初始化完成后,执行 AOP 代理创建:

  1. 检查当前 Bean 是否匹配切点(PointCut)

  2. 匹配 → 生成代理对象,替换原始 Bean

  3. 不匹配 → 使用原始 Bean

  4. 代理对象存入 Spring 容器

阶段 3:方法调用执行

  1. 代码中 @Autowired 注入的是代理对象

  2. 调用方法时,代理对象先执行通知逻辑

  3. 再通过反射调用原始 Bean 的目标方法

  4. 最后执行剩余通知

相关推荐
QuZhengRong1 小时前
【Luck-Report】缓存
java·前端·后端·vue·excel
XiYang-DING1 小时前
【Spring】SpringMVC
java·后端·spring
想学习java初学者1 小时前
SpringBoot整合GS1编码解码
java·spring boot·后端
日月云棠1 小时前
2 快速入门实战指南
java·后端
日月云棠1 小时前
3 Dubbo 2.7 高级配置:检查控制、版本策略与协议选择
java·后端
砍材农夫1 小时前
物联网 基于netty构建mqtt协议规范(主题通配符订阅)
java·前端·javascript·物联网·netty
掉鱼的猫1 小时前
用 Solon AI 从零构建 MCP 工具服务:让 AI Agent 拥有真实世界的能力
java·llm·mcp
日月云棠1 小时前
1 分布式架构演进与Dubbo框架入门
java·后端
彩票管理中心秘书长1 小时前
智能体状态指示:何时思考、何时调用工具、何时出错
前端·后端·程序员