【硬核实战】Spring AOP 从原理到落地:3 个可运行案例带你吃透切面编程
作者风格MY_TEUCK 硬核实战风 AI 应用落地风读者对象Java 学习者 / Spring Boot 初中级开发者阅读目标不仅知道 AOP 是什么更知道什么时候用、怎么写、怎么避坑前言很多同学第一次学 AOP面向切面编程会有两个误区只会背概念Aspect、Pointcut、Advice但写不出可用代码。只会抄例子日志打印能跑但一到业务场景权限、幂等、审计就不会落地。这篇文章直接走实战路线先把思想讲透再给你可运行代码最后落到真实业务场景和面试答法确保你学完就能用。一、概念解析AOP 到底是什么1.1 一句话定义AOP 是把“横切关注点”从业务代码中抽离出来统一管理的一种编程思想。什么叫横切关注点不是核心业务本身比如下单、支付、查询订单但很多地方都要做比如日志、权限、事务、审计、幂等1.2 为什么需要 AOP如果不用 AOP代码常变成这样每个方法都写一遍日志每个接口都写一遍权限判断每次更新都手动补审计字段结果是重复、易漏、难维护。1.3 AOP 与 OOP 的关系OOP负责纵向建模订单服务、商品服务、用户服务AOP负责横向增强日志、权限、监控、审计它们不是替代关系而是互补关系。二、原理Spring AOP 是怎么工作的Spring AOP 的核心不是“魔法”而是代理模式。2.1 执行链路Spring 启动扫描到切面Aspect根据切点Pointcut匹配目标方法为目标 Bean 创建代理对象JDK 动态代理 / CGLIB你调用的是代理对象代理在合适时机执行通知Advice再执行目标方法2.2 关键术语必须会Aspect切面承载增强逻辑的类Pointcut切点定义拦截哪些方法Advice通知定义何时增强前置/后置/环绕/异常JoinPoint连接点可被增强的方法执行点Weaving织入把切面应用到目标对象的过程三、实战代码3 个可运行 AOP 案例下面示例基于 Spring Boot直接可用。3.1 准备依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependency3.2 案例一接口耗时统计最直观1切面代码packagecom.example.aop;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.springframework.stereotype.Component;AspectComponentSlf4jpublicclassCostTimeAspect{Around(execution(* com.example.service..*(..)))publicObjectrecordTime(ProceedingJoinPointpjp)throwsThrowable{longstartSystem.currentTimeMillis();try{returnpjp.proceed();}finally{longcostSystem.currentTimeMillis()-start;log.info(method{} cost{}ms,pjp.getSignature().toShortString(),cost);}}}2业务代码packagecom.example.service;importorg.springframework.stereotype.Service;ServicepublicclassOrderService{publicStringcreateOrder()throwsInterruptedException{Thread.sleep(120);returnok;}}3.3 案例二自定义注解 权限校验1定义注解packagecom.example.aop;importjava.lang.annotation.*;Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceRequiresRole{Stringvalue();}2切面校验packagecom.example.aop;importorg.aspectj.lang.JoinPoint;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.stereotype.Component;AspectComponentpublicclassRoleCheckAspect{Before(annotation(com.example.aop.RequiresRole))publicvoidcheckRole(JoinPointjoinPoint){MethodSignaturesignature(MethodSignature)joinPoint.getSignature();RequiresRolerequiresRolesignature.getMethod().getAnnotation(RequiresRole.class);StringneedRolerequiresRole.value();// 示例真实项目可从 ThreadLocal / JWT 中取当前用户角色StringcurrentRoleUSER;if(!needRole.equals(currentRole)){thrownewRuntimeException(无权限访问needRoleneedRole);}}}3使用注解packagecom.example.service;importcom.example.aop.RequiresRole;importorg.springframework.stereotype.Service;ServicepublicclassAdminService{RequiresRole(ADMIN)publicStringdeleteUser(){returndeleted;}}3.4 案例三公共字段自动填充企业项目高频这个场景是很多管理系统都存在的真实需求新增时自动填充createTime/createUser/updateTime/updateUser更新时自动填充updateTime/updateUser1注解 操作类型publicenumOperationType{INSERT,UPDATE}Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceAutoFill{OperationTypevalue();}2切面核心逻辑AspectComponentpublicclassAutoFillAspect{Before(execution(* com.example.mapper..*(..)) annotation(com.example.aop.AutoFill))publicvoidautoFill(JoinPointjoinPoint)throwsException{MethodSignaturesignature(MethodSignature)joinPoint.getSignature();AutoFillautoFillsignature.getMethod().getAnnotation(AutoFill.class);OperationTypetypeautoFill.value();ObjectentityjoinPoint.getArgs()[0];LocalDateTimenowLocalDateTime.now();LongcurrentUserId1L;// 示例真实项目从上下文取if(typeOperationType.INSERT){entity.getClass().getMethod(setCreateTime,LocalDateTime.class).invoke(entity,now);entity.getClass().getMethod(setCreateUser,Long.class).invoke(entity,currentUserId);}entity.getClass().getMethod(setUpdateTime,LocalDateTime.class).invoke(entity,now);entity.getClass().getMethod(setUpdateUser,Long.class).invoke(entity,currentUserId);}}四、场景案例AOP 在业务中怎么选4.1 订单系统下单接口幂等切面防重复提交核心服务耗时切面定位慢调用管理接口权限切面拦截越权操作4.2 后台管理系统数据变更操作审计切面谁、何时、改了什么增删改接口统一日志切面通用字段自动填充切面4.3 AI 应用落地场景在 AI 业务里AOP 同样有价值模型调用耗时统计推理耗时、token 消耗Prompt 安全校验敏感词、越权指令统一打点埋点命中率、错误率、重试次数重点AOP 不是传统业务专属在 AI 工程化里同样是“横向治理利器”。五、避坑指南实战高频5.1 自调用失效同类中this.xxx()调用可能绕过代理导致切面不生效。解决通过代理对象调用或拆分到其他 Bean。5.2 切点写错写太宽误伤无关方法写太窄该拦截的没拦截建议先在切面里打日志确认命中范围。5.3 多切面顺序混乱多个切面叠加时用Order明确顺序避免执行链不可控。5.4 环绕通知漏写proceed()Around不调用proceed()目标方法不会执行。这个坑非常常见。5.5 反射异常未处理自动填充常用反射setter 名称、参数类型要严格一致异常处理要完整。六、面试题高频 可直接回答题 1AOP 的核心价值是什么把横切关注点日志、权限、事务等从业务逻辑中剥离减少重复代码提高一致性和可维护性。题 2Spring AOP 和动态代理有什么关系Spring AOP 基于代理模式实现增强目标对象被代理后在方法执行前后插入通知逻辑。题 3Before和Around怎么选只做前置校验Before需要统计耗时、控制是否执行原方法Around题 4为什么事务有时不生效常见原因是自调用绕过代理、方法不是public、异常被吞掉或配置不当。题 5AOP 适合所有逻辑吗不适合。只建议用于跨模块、规则统一、与业务相对解耦的逻辑。总结AOP 真正的价值不是“写了个切面”而是建立一套工程化思维把重复横向逻辑统一治理让业务代码专注核心业务让系统更稳定、更可维护、更易扩展你可以从这条落地路径开始先做耗时统计切面最快看到收益再做权限切面业务价值高最后做审计/幂等/自动填充系统化治理当你把 AOP 用到“看不到它但处处受益”的程度你就真正掌握它了。