SSM - Spring_day03 - 2.AOP入门案例

2,AOP入门案例

2.1 需求分析

案例设定:测算接口执行效率,但是这个案例稍微复杂了点,我们对其进行简化。

简化设定:在方法执行前输出当前系统时间。

对于SpringAOP的开发有两种方式,XML 和 ==注解==,我们使用哪个呢?

因为现在注解使用的比较多,所以本次课程就采用注解完成AOP的开发。

总结需求为:使用SpringAOP的注解方式完成在方法执行的前打印出当前系统时间。

🧠 理论理解

这个案例的核心目标是让方法在执行之前自动打印系统时间,这实际上是横切关注点的经典场景。所谓横切关注点指的是:和业务逻辑无关、但需要重复出现在多处的功能(如日志、安全检查、事务控制等)。AOP 的出现,就是为了解决这种重复代码侵入问题,让增强逻辑独立封装。

🏢 企业实战理解

  • 字节跳动:在大规模微服务中,每个接口访问都要打监控日志,AOP 用于统一拦截请求参数、请求时间、响应时间。

  • 美团:订单系统需要对高价值交易接口记录入参、出参及耗时,使用 AOP 完成非侵入式监控。

  • Google Cloud:API 网关层基于 AOP 实现访问审计,自动记录接口调用日志及安全验证。

 

面试题 1:为什么我们要用 AOP 实现“在方法执行前打印时间戳”这种功能?直接写在方法内部不行吗?AOP 的优势在哪?

参考答案:

这个问题其实是想确认你有没有真正理解 AOP 的核心价值。

如果直接在方法里写时间戳打印当然可以实现功能,但存在几个问题:

1️⃣ 不符合单一职责原则。业务方法本来应该只关注业务逻辑,如果混入非核心逻辑(如日志、性能监控),代码可读性和可维护性会变差。

2️⃣ 不利于复用。当我们想给多个方法加类似的“时间戳”功能时,必须每个方法都手写,非常繁琐。

3️⃣ 不利于后期维护。如果需要修改日志输出格式或逻辑,必须到每个方法里去改,风险高。

AOP 的优势:

  • 能实现“无侵入式增强”,即不改原始代码就能动态扩展功能。

  • 通用功能(如日志、权限、事务)可以抽取复用,一次编写、全局生效

  • 支持按需绑定切入点,实现灵活精细控制

总结一句话:AOP 是为了“横切关注点分离”,解耦非核心功能,让业务逻辑更加纯粹,这是企业项目必须具备的架构能力。

场景题 1

🚗 业务背景:
你在字节跳动负责一个“内容风控”模块。某天,产品经理提出:

“现在内容审核服务的 save 方法要打印每次调用的开始时间和调用结束时间,并统计接口耗时,但又不想改动已有的业务逻辑。”

你如何设计?

解答思路:

这是典型的无侵入式增强场景,特别适合用 AOP 解决。

我的做法:
1️⃣ 编写一个 LogAdvice 类,里面实现一个 @Around 通知,逻辑:

  • 方法执行前记录开始时间;

  • 方法执行后记录结束时间,并计算耗时。

2️⃣ 切入点表达式锁定内容审核模块的 save() 方法,比如:

@Pointcut("execution(* com.bytedance.contentreview.*.save(..))")

3️⃣ 切面使用 @Aspect 声明,配置类加上 @EnableAspectJAutoProxy 启动 AOP。

优势:

  • 不改任何已有业务代码;

  • 可以随时扩展到其他方法(如 update());

  • 满足高并发场景下的日志追踪要求。

这是字节级别常见的业务操作:用 AOP 管控非核心逻辑,保持业务代码干净。

 

2.2 思路分析

需求明确后,具体该如何实现,都有哪些步骤,我们先来分析下:

1.导入坐标(pom.xml)

2.制作连接点(原始操作,Dao接口与实现类)

3.制作共性功能(通知类与通知)

4.定义切入点

5.绑定切入点与通知关系(切面)

🧠 理论理解

AOP 的底层机制本质是:将业务代码(连接点)增强逻辑(通知)通过织入机制动态结合起来。Spring 实现 AOP 时,是通过 JDK 动态代理(接口) 和 **CGLIB 动态代理(类)**两种方式完成。

🏢 企业实战理解

  • 阿里巴巴:其分布式事务方案 Seata 中,事务拦截器本质也是通过 AOP 思路,将事务增强逻辑织入业务代码。

  • NVIDIA:云 GPU 任务管理系统在任务调度和执行环节,通过 AOP 增强了细粒度的性能监控。

  • OpenAI:API 平台会用 AOP 式编程来做 IP 黑名单检查、速率限制,这样接口逻辑无需关心“安全检测”的实现细节。

 

面试题 2:在 Spring AOP 开发中,切入点、通知、切面之间是什么关系?可以用一句话概括吗?

参考答案:

我通常用一句话总结它们的关系:
👉 “切入点是要增强的地方,通知是增强的内容,切面是二者的结合。”

详细解释是这样的:

  • 切入点(Pointcut):定义哪些方法需要被增强,比如 BookDao.update()

  • 通知(Advice):就是增强的具体逻辑,比如“打印当前时间戳”这个功能。

  • 切面(Aspect):把切入点和通知绑在一起,形成一条完整的 AOP 规则。

MyAdvice 这个例子里:

  • pt() 方法是切入点,

  • method() 方法是通知,

  • MyAdvice 整个类是切面。

这也是面试官喜欢追问的地方,因为只有真正掌握了三者关系,才能灵活运用 AOP。

场景题 2

🏦 业务背景:
你在支付宝做支付网关系统,网关层有很多 RPC 接口。某天团队想实现一个“调用追踪 ID 机制”:

每个接口请求进来时生成一个唯一 traceId,贯穿整个日志链路,方便排查问题。

要求是:

  • traceId 必须写入 MDC(日志上下文);

  • 所有 Controller 方法都自动加上 traceId,无需手动编码。

解答思路:

这种场景也是AOP 非常合适的典型应用。

设计方案:
1️⃣ 定义 TraceAdvice 类,通知类型选择 @Before@Around

  • 生成 UUID 类型的 traceId;

  • 写入 MDC.put("traceId", id)

  • 日志配置文件中追加 %X{traceId} 打印 traceId。

2️⃣ 切入点表达式选取所有 @RestController 下的方法:

@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")

3️⃣ 切面打上 @Aspect 注解,统一生效。

好处:

  • 全链路追踪无感知,Controller 不用关心 traceId;

  • 如果有上游传入 traceId,还可以做透传;

  • 满足高并发 & 分布式环境下的诊断需求。

这种网关级别的 AOP 场景,在蚂蚁金服、腾讯云等大厂都是标配。

 

2.3 环境准备

  • 创建一个Maven项目

  • pom.xml添加Spring依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>

  • 添加BookDao和BookDaoImpl类

    public interface BookDao {
        public void save();
        public void update();
    }
    ​
    @Repository
    public class BookDaoImpl implements BookDao {
    ​
        public void save() {
            System.out.println(System.currentTimeMillis());
            System.out.println("book dao save ...");
        }
    ​
        public void update(){
            System.out.println("book dao update ...");
        }
    }

  • 创建Spring的配置类

    @Configuration
    @ComponentScan("com.itheima")
    public class SpringConfig {
    }

  • 编写App运行类

    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            BookDao bookDao = ctx.getBean(BookDao.class);
            bookDao.save();
        }
    }

最终创建好的项目结构如下:

 

说明:

  • 目前打印save方法的时候,因为方法中有打印系统时间,所以运行的时候是可以看到系统时间

  • 对于update方法来说,就没有该功能

  • 我们要使用SpringAOP的方式在不改变update方法的前提下让其具有打印系统时间的功能。

🧠 理论理解

这里是 AOP 的“连接点”准备阶段——定义了一个 BookDao 接口及其实现类,形成原始业务代码。这也是 Spring AOP 的基础:Spring 只能拦截Spring 容器管理的 Bean,因此要保证 Dao/Service 类通过注解或 XML 纳入 IoC。

🏢 企业实战理解

  • 腾讯云:治理网关接口时,接口本体代码通过 Spring 托管,AOP 切入做熔断、限流、日志。

  • 美团:所有微服务模块必须实现接口规范,并纳入 Spring IoC 管理,确保 AOP 监控工具链可全局适配。

 

面试题 3:项目中为什么推荐用注解方式做 AOP 而不是 XML?它们各自的优缺点是什么?

参考答案:

目前业界(特别是字节、阿里这类大厂)更推荐注解方式,原因主要是:

✔️ 优点:

  • 配置简洁,代码和 AOP 配置紧耦合,一眼就能看出哪些类是切面;

  • 配合 Spring Boot 非常友好(Boot 就是无 XML 化的代表);

  • 维护方便,不需要在 XML 中来回找配置。

缺点:

  • 注解方式侵入代码(虽然不改业务逻辑,但切面类上必须加注解),有时候不符合“零入侵”要求;

  • 如果项目极度模块化,注解散落在各个包里,难以统一管理。

XML 的优点:

  • 完全解耦代码,所有 AOP 逻辑集中在 XML 文件中;

  • 某些需要动态切换切面实现的场景下更方便。

**我的实际经验是:**日常项目(比如日志、权限、监控)几乎清一色用注解;但一些对“无代码入侵”要求特别高的核心系统,还是会看到 XML 的影子。

2.4 AOP实现步骤

步骤1:添加依赖

pom.xml

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

 

  • 因为spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop

  • 导入AspectJ的jar包,AspectJ是AOP思想的一个具体实现,Spring有自己的AOP实现,但是相比于AspectJ来说比较麻烦,所以我们直接采用Spring整合ApsectJ的方式进行AOP开发。

步骤2:定义接口与实现类
环境准备的时候,BookDaoImpl已经准备好,不需要做任何修改
步骤3:定义通知类和通知

通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印。

public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

类名和方法名没有要求,可以任意。

步骤4:定义切入点

BookDaoImpl中有两个方法,分别是save和update,我们要增强的是update方法,该如何定义呢?

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

说明:

  • 切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑。

  • execution及后面编写的内容,后面会有章节专门去学习。

步骤5:制作切面

切面是用来描述通知和切入点之间的关系,如何进行关系的绑定?

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行==位置==

 

说明:@Before翻译过来是之前,也就是说通知会在切入点方法执行之前执行,除此之前还有其他四种类型,后面会讲。

步骤6:将通知类配给容器并标识其为切面类
@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
步骤7:开启注解格式AOP功能
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
步骤8:运行程序
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }
}

看到在执行update方法之前打印了系统时间戳,说明对原始方法进行了增强,AOP编程成功。

 

知识点1:@EnableAspectJAutoProxy

名称@EnableAspectJAutoProxy
类型配置类注解
位置配置类定义上方
作用开启注解格式AOP功能

知识点2:@Aspect

名称@Aspect
类型类注解
位置切面类定义上方
作用设置当前类为AOP切面类

知识点3:@Pointcut

名称@Pointcut
类型方法注解
位置切入点方法定义上方
作用设置切入点方法
属性value(默认):切入点表达式

知识点4:@Before

名称@Before
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
步骤1️⃣ 添加依赖

🧠 理论理解

引入 aspectjweaver 是因为:Spring AOP 本身只是“API 规范”,但底层的“织入”能力主要依赖 AspectJ。这是一种面向切面编程的专业实现方案。

🏢 企业实战理解

  • 阿里巴巴:自研中间件如 Sentinel、RocketMQ 也大量用 AspectJ 做埋点。

  • 字节跳动:日志追踪框架通过 AspectJ 动态织入日志逻辑,支持无感埋点。


步骤3️⃣ 定义通知类

🧠 理论理解

通知类是 AOP 的“增强逻辑”,和业务代码解耦。比如这里定义的 method(),它封装了“打印时间”的功能。AOP 允许将多个通知组合起来形成“复合增强”。

🏢 企业实战理解

  • 京东:风控监控会通过通知类封装“参数验证”和“风险检测”逻辑,复用性强。

  • OpenAI:API 层面的 Token 认证、限流模块都通过 AOP 通知机制实现,减少接口内重复代码。


步骤5️⃣ 制作切面

🧠 理论理解

切面是“织入”的桥梁,它描述“谁增强谁”,即把“切入点”与“通知”绑定起来。切面实现了“横向关注点”和“纵向业务代码”的解耦与协作

🏢 企业实战理解

  • 滴滴出行:高并发调度系统中,使用切面批量绑定了所有高频接口,自动加上防刷逻辑。

  • 字节跳动:在服务启动时批量加载切面,实现全链路请求日志+监控闭环。


步骤7️⃣ 开启 AOP 功能

🧠 理论理解

@EnableAspectJAutoProxy 本质是启动 AOP 基础设施,开启 Spring 的代理创建流程。没这个注解,Spring 不会帮你织入增强逻辑。

🏢 企业实战理解

  • 华为云:Spring Cloud 基础设施会自动开启 AOP 代理支持,方便埋点、监控等功能全局启用。

  • 美团:微服务平台规范中强制所有服务必须启用 AOP,方便统一治理和跨系统追踪。


知识点理解

@Aspect

🧠 理论理解:标记一个类是“切面类”,AOP 扫描器才会去解析它的切入点和通知。
🏢 实战:大厂中所有日志、事务、埋点、权限模块都会做成切面类,统一挂载到 IoC 容器。

@Pointcut

🧠 理论理解:定义“哪些方法”需要增强,它是切入点表达式的载体。
🏢 实战:企业中通常写成全局表达式,比如“所有Controller层的方法”统一做权限校验。

@Before

🧠 理论理解:定义通知执行的时机——这里是“在目标方法执行前触发”。
🏢 实战:字节的网关层 AOP 用 @Before 做安全校验,OpenAI 用 @Before 实现 IP 黑名单拦截。

步骤1

面试题 4:Spring 为什么选择集成 AspectJ 而不是自己单独实现 AOP 框架?AspectJ 和 Spring AOP 的关系是什么?

参考答案:

这个问题挺有深度。首先,Spring 确实自己实现了 AOP 核心功能(基于动态代理),但它的内置 AOP 只支持方法级别增强,而且语法比较麻烦。

AspectJ 是面向切面编程领域的“标杆”实现:

  • 功能丰富,支持方法、字段、构造器、静态块等各种切入点;

  • 语法成熟,execution() 表达式成为事实标准。

Spring 选择集成 AspectJ(而不是重复造轮子),有两个主要原因:
1️⃣ 生态成熟,社区接受度高;
2️⃣ 通过“Spring+AspectJ”的方式能提供最广泛的 AOP 支持,兼顾灵活性和易用性。

所以说:

  • Spring AOP 是一个框架

  • AspectJ 是一个工具库

  • Spring AOP + AspectJ 联手解决了“企业级 AOP”的痛点。


步骤5

面试题 5:@Before、@After、@Around 这几个通知注解在实际项目中该怎么选择?@Around 有什么特别之处?

参考答案:

这几个注解的区别主要是“增强逻辑的执行时机”:

  • @Before:目标方法执行调用;

  • @After:目标方法执行调用(无论是否异常);

  • @Around环绕通知,能包裹整个目标方法,最强大

@Around 特别之处:

  • 它不仅能在方法执行前/后做操作,还能控制方法是否执行,甚至修改返回值

  • 实际上,它可以完全替代其他四种通知类型。

项目实战:

  • 一般情况下,日志、审计这种需求用 @Before@After 就够了;

  • 安全控制(如权限检查)推荐用 @Around,因为可以在检测不通过时直接中断方法执行

  • 性能监控/事务控制场景,也通常选择 @Around

大厂面试经常问你怎么“权衡选择通知类型”,这个角度就是重点。


步骤7

面试题 6:为什么需要加 @EnableAspectJAutoProxy?它底层是怎么做到开启 AOP 能力的?

参考答案:

@EnableAspectJAutoProxyAOP 的入口注解,如果不加它,Spring 根本不会扫描 @Aspect 注解,也就不会生成代理对象。

底层原理:

  • @EnableAspectJAutoProxy 内部触发 AspectJAutoProxyRegistrar

  • 它会向 Spring 容器注册 AnnotationAwareAspectJAutoProxyCreator 这个关键的 BeanPostProcessor

  • 这个后置处理器会在 Bean 初始化后,判断是否需要生成代理对象,从而实现 AOP。

一句话总结:它是 AOP 整个机制启动的“钥匙”。

 

场景题 3

🛒 业务背景:
你在美团外卖做订单系统,老板要求监控“高频访问接口”的性能,比如 submitOrder() 方法,希望实时知道哪个时间段接口慢、慢在哪。

要求:

  • 统计执行时间;

  • 如果超过阈值(如 300ms),在日志中报警。

解答思路:

典型的 AOP + 性能监控场景。

我的实现步骤:
1️⃣ 定义 PerformanceAdvice,用 @Around 实现环绕通知:

  • 方法开始时记录 System.nanoTime()

  • 方法执行完后再次取时间,计算耗时。

2️⃣ 判断是否超过阈值:

if (executionTime > 300) {
    log.warn("【慢接口报警】方法:{},耗时:{}ms", joinPoint.getSignature(), executionTime);
}

3️⃣ 切入点锁定高频接口:

@Pointcut("execution(* com.meituan.order.*.submitOrder(..))")

4️⃣ 日志采集可配合监控系统(如 Prometheus + Grafana)实现自动告警。

优势:

  • 动态发现性能瓶颈;

  • 报警信息细粒度,精准到方法级别;

  • 不入侵业务逻辑,后续调整阈值也简单。

这是大厂日常会做的“接口 SLA 保证”场景,AOP 是必选方案。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值