AOP(面向切面编程)

发布时间:2026/7/20 11:19:30
AOP(面向切面编程) 一、大白话AOP 到底是干嘛的不用技术词类比假设你开发一套外卖商家后台系统你是后台使用者/开发人员系统核心功能下单、退款、改价、导出订单、删除商品。通用重复需求每个操作都要做操作前校验登录、权限操作前打印请求参数日志操作后记录操作人、操作时间、操作内容操作日志方法报错时捕获异常、发送告警短信统计每个接口执行耗时慢接口记录下来1. 不用 AOP 的原始写法痛点每个业务方法里手动复制粘贴权限校验、打日志、异常捕获、计时代码。代码重复几万行改一个规则要全项目改漏改就出 bug业务代码下单逻辑和非业务代码日志、权限揉在一起可读性极差2. AOP 切面解决方案核心作用AOP 面向切面编程把所有方法共用的「通用附属功能」抽离成独立一块切面 Aspect不改动原有业务代码自动在目标方法执行前后 / 异常时插入通用逻辑。一句话总结业务代码只写核心业务日志、权限、监控、事务、限流全部交给切面统一处理。3. AOP 四大核心作用软件开发日常使用场景日志统一处理请求日志、操作日志、异常日志不用每个方法手写权限 / 登录校验所有接口统一拦截未登录 / 无权限直接拦截不进业务逻辑事务管理方法出错自动回滚数据库Spring 事务底层就是 AOP监控统计接口耗时、请求次数、异常次数统计、接口限流统一异常处理、参数校验、数据脱敏、接口缓存二、AOP 完整 5 种通知类型执行时机 使用场景 通俗解释AOP 通知就是切面代码执行的 5 个时间点全部列举区分使用场景执行顺序重点正常无异常Around前置代码 → Before → 执行业务方法 → AfterReturning → After → Around后置代码出现异常Around前置代码 → Before → 业务报错 → AfterThrowing → After三、完整可运行代码示例Spring Boot AOP最常用场景说明模拟商家后台订单管理模块切面统一实现登录校验、打印入参、操作日志、异常告警、接口耗时统计业务类OrderService包含下单、退款两个核心业务方法切点规则拦截所有 com.demo.service 下所有类的所有 public 方法1. 依赖Maven!-- Spring AOP 核心依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency2. 业务 Service核心业务无任何日志/权限代码干净纯粹package com.demo.service; import org.springframework.stereotype.Service; Service public class OrderService { // 下单业务 public String createOrder(Long userId, Long goodsId, Integer num) { System.out.println(【业务逻辑】创建订单用户 userId); // 模拟数据库新增订单 return 订单创建成功订单号OD20260719001; } // 退款业务模拟异常场景 public String refundOrder(Long orderId) { System.out.println(【业务逻辑】执行退款操作); // 模拟异常订单不存在抛出错误 if(orderId 1000) { throw new RuntimeException(订单编号不存在退款失败); } return 退款成功; } }3. AOP 切面类统一处理所有通用逻辑完整覆盖 5 种通知package com.demo.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.time.LocalDateTime; // 标记为切面类 Spring容器管理 Aspect Component public class OperationAspect { // 切点表达式定义拦截范围 // execution(返回值 包路径.类.方法(参数)) // * com.demo.service.*.*(..) // 第一个*任意返回值 // com.demo.service.*service包下所有类 // 第二个*类中所有方法 // (..)任意参数 Pointcut(execution(* com.demo.service.*.*(..))) public void servicePointCut() {} // 1. Before 前置通知方法执行前 // 使用场景登录校验、权限拦截、打印请求参数 Before(servicePointCut()) public void before(JoinPoint joinPoint) { System.out.println(\n 【Before 前置通知执行】 ); // 获取方法名 String methodName joinPoint.getSignature().getName(); // 获取入参 Object[] args joinPoint.getArgs(); System.out.println(即将执行方法 methodName); System.out.println(方法入参); for (Object arg : args) { System.out.print(arg ); } // 模拟权限校验逻辑 Long loginUserId null; // 从token获取登录人 if(loginUserId null) { throw new RuntimeException(未登录禁止操作); } } // 2. AfterReturning 返回通知方法正常跑完无异常 // 使用场景记录操作日志、获取返回结果 // returning result 用来接收方法返回值 AfterReturning(pointcut servicePointCut(), returning result) public void afterReturn(JoinPoint joinPoint, Object result) { System.out.println(\n 【AfterReturning 返回通知执行】 ); String method joinPoint.getSignature().getName(); System.out.println(方法【 method 】执行成功); System.out.println(方法返回结果 result); // 模拟写入数据库操作日志 System.out.println(记录操作日志时间 LocalDateTime.now() 操作内容 method); } // 3. AfterThrowing 异常通知方法报错时执行 // 使用场景异常告警、记录错误堆栈 // throwing ex 接收抛出的异常对象 AfterThrowing(pointcut servicePointCut(), throwing ex) public void afterThrow(JoinPoint joinPoint, Exception ex) { System.out.println(\n 【AfterThrowing 异常通知执行】 ); String method joinPoint.getSignature().getName(); System.out.println(方法【 method 】发生异常); System.out.println(异常信息 ex.getMessage()); // 模拟发送短信告警给管理员 System.out.println(发送告警短信订单模块方法报错); } // 4. After 后置通知无论成功失败一定执行 // 使用场景释放资源、清理临时数据 After(servicePointCut()) public void after() { System.out.println(\n 【After 后置通知执行】 ); System.out.println(方法执行完毕清理临时缓存数据); } // 5. Around 环绕通知功能最强包裹整个方法 // 使用场景统计耗时、接口限流、缓存、动态修改参数/返回值 Around(servicePointCut()) public Object around(ProceedingJoinPoint point) throws Throwable { System.out.println(\n 【Around 环绕前置代码】 ); long start System.currentTimeMillis(); // 执行目标业务方法必须调用proceed()否则业务方法不会运行 Object result point.proceed(); // 方法执行完成后逻辑 long end System.currentTimeMillis(); long cost end - start; System.out.println(\n 【Around 环绕后置代码】 ); System.out.println(方法执行耗时 cost ms); // 模拟慢接口监控 if(cost 100) { System.out.println(警告该接口执行缓慢耗时超过100ms); } return result; } }4. 测试启动类package com.demo; import com.demo.service.OrderService; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; SpringBootApplication public class AopDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context SpringApplication.run(AopDemoApplication.class, args); OrderService orderService context.getBean(OrderService.class); System.out.println( 测试1正常下单方法 ); orderService.createOrder(1001L, 5002L, 2); System.out.println(\n 测试2退款方法会抛异常 ); try { orderService.refundOrder(999L); } catch (Exception e) { // 捕获切面抛出的异常防止程序终止 } } }四、运行结果拆解直观看懂 5 种通知执行顺序测试 1正常下单无异常执行顺序打印节选Around 环绕前置代码Before 前置通知打印入参、权限校验执行业务方法 createOrderAfterReturning 返回通知记录日志、打印返回值After 后置通知清理缓存Around 环绕后置代码统计耗时测试 2退款抛出异常执行顺序打印节选Around 环绕前置代码Before 前置通知执行业务方法 refundOrder → 抛出异常AfterThrowing 异常通知记录报错、发告警After 后置通知一定会执行Around 不会执行后置代码方法报错中断五、分类型详细使用场景什么时候用哪一种完整列举1. Before 前置通知执行前适用场景接口统一登录 token 校验未登录直接抛出异常拦截请求参数统一校验参数为空、格式错误提前拦截打印所有接口请求参数日志接口访问频率前置限流判断不适合需要拿到返回值、处理异常、统计耗时2. AfterReturning 返回通知正常成功后适用场景新增 / 修改 / 删除操作后记录数据库操作日志获取接口返回数据做数据脱敏手机号、身份证隐藏业务成功后发送消息通知下单成功推送消息限制只有方法无异常才会执行报错不会进入3. AfterThrowing 异常通知报错时适用场景全局捕获业务异常统一记录异常堆栈日志系统报错发送短信 / 钉钉告警给运维出错时清理临时文件、回滚缓存数据限制只有抛出异常才执行正常执行不会触发4. After 后置通知无论成败必执行适用场景关闭 IO 流、数据库临时连接、释放锁资源类似 try-finally请求结束统一清除 ThreadLocal 本地存储数据不管成功失败都记录请求访问记录5. Around 环绕通知全能型优先级最高适用场景最常用统计所有接口执行耗时、监控慢接口接口缓存先查缓存有数据直接返回不执行业务方法动态修改入参、修改方法返回结果自定义限流、分布式锁控制手动控制事务手动提交 / 回滚优势唯一可以控制目标方法是否执行的通知可以在 proceed() 前后自由写逻辑缺点代码相对复杂忘记调用 proceed() 会导致业务逻辑完全不执行六、关键点1. 切点 Pointcut 作用用来定义哪些类 / 哪些方法需要被切面拦截支持包、类、注解、方法名匹配。常用切点示例// 拦截加了Log注解的所有方法自定义注解切面最常用 Pointcut(annotation(com.demo.annotation.Log)) // 拦截Controller层所有接口 Pointcut(execution(* com.demo.controller.*.*(..)))2. AOP 底层原理通俗Spring AOP 基于动态代理实现接口的类 → JDK 动态代理没有实现接口的类 → CGLIB 字节码代理程序启动时自动生成目标类的代理对象调用方法时先走切面逻辑再走原业务代码完全不用修改原有业务代码。3. 日常开发高频 AOP 落地场景汇总操作日志框架所有后台管理系统必备Spring 声明式事务Transactional 底层 AOP接口防重复提交统一接口限流全局数据脱敏接口性能监控、埋点统计权限拦截、RBAC 权限校验全局异常统一封装返回4. 核心优势总结站在开发使用者角度解耦业务代码和通用辅助代码分离业务逻辑干净复用一套切面逻辑全局所有方法共用不用重复复制易维护修改日志 / 权限规则只改切面一处全项目生效无侵入不用改动原有业务代码新增功能零风险