Aop切面
1注解怎么被 Spring 用上的你是否知道注解本身只是“标记”本身没有任何功能2它会扫所有代码吗你是否知道 Spring 只扫自己的 Beanclasspath 扫描 BeanPostProcessor3别的注解加载方式一样吗你是否知道不同注解由不同的处理器处理Transactional、Async 都走 AOPAutowired 走注入不是一套机制4扫描之后它做了什么工作核心你是否知道是生成了代理对象5代码不会跳着执行怎么把两部分代码跑完的逻辑推理题——逼你自己推导出“肯定有个东西在中间调用了两边”6为什么有 before / after逼你想通切面代码和业务代码在同一个调用链里所以才有顺序概念1.总体介绍代理模式是一种结构型设计模式不直接访问目标对象而是通过一个“代理对象”来间接访问代理在访问前后可以附加额外的逻辑。Spring AOP 的本质就是在运行时用 JDK 动态代理或 CGLIB 给目标 Bean 生成一个代理对象容器里放的是代理对象你调用方法时实际走的是代理代理帮你插入切面逻辑。你写的: userService.save() 实际执行: 代理.save() → 前置通知 → 目标save() → 后置通知 → 返回为什么 AOP 要使用代理模式核心你问“其实就是为了插入一段代码为什么要引入代理模式”这是很多初学者的困惑。答案为了无侵入和开闭原则。如果不用代理直接“插入代码”只有两种粗暴方式硬编码在业务方法里手动写日志、事务代码侵入性强改业务代码要改切面代码。修改字节码直接用 ASM 等工具修改.class文件复杂度高难以管理。代理模式的价值在于它创造了一个“中间层”。这个中间层像一个“包装盒”把原始对象包起来。调用者拿到包装盒以为用的是原对象但实际走的是包装盒的流程。这样原始业务类核心逻辑完全不知道有切面存在无侵入。切面逻辑日志、事务完全抽离可以随时增减符合开闭原则。这本质上是“控制反转”思想的延伸你不直接调目标方法而是把控制权交给代理由代理决定何时调、怎么调。核心注解1.声明切片类Aspect // 声明我是切面类Component // ⚠️ 必须搭配否则 Spring 根本不管理它 Bean2.定义切点Pointcut(execution(* com.demo.service.*.*(..)))public void myPointcut() {} // 方法体永远为空纯粹是切点的变量名3.注解通知注解 执行时机 执行位置Before 方法执行前 无法访问返回值After 方法执行后finally 语义无论成败都执行 —AfterReturning 方法正常返回后 可拿到返回值AfterThrowing 方法抛异常后 可拿到异常对象Around 环绕包裹整个方法最强可控制是否执行 可修改参数/返回值4.自定义注解Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)public interface MyLog { }个人理解创建一个自定义注解一个切面类Aspect,一个目标类都有bean标识启动项目后扫描录入切面类和目标类后查看切面类发现其中的annotation了解到有一自定义注解倒回去查看bean中有哪个类使用了这一自定义注解发现我的目标类然后创建代理对象代理执行例如前后加日志中间让源对象执行原始逻辑① Spring 启动 → 扫描所有 Bean包括 Aspect 标记的切面类② Spring 发现切面类中 Around(annotation(LogAnnotation)) 的切点表达式③ Spring 检查容器里所有 Bean哪些方法上有 LogAnnotation④ 匹配到 OrderService.createOrder()⑤ 在创建 OrderService 这个 Bean 的最后一步BeanPostProcessor用动态代理把原始对象包装成代理对象⑥ 容器里实际存放、注入给别人的是这个代理对象日志场景完整示例自定义注解Target(ElementType.METHOD)//方法类型 Retention(RetentionPolicy.RUNTIME)//保留到运行时 public interface LogExecution { String value() default ; }切面类Aspect Component public class LogAspect { private static final Logger log LoggerFactory.getLogger(LogAspect.class); Around(annotation(logAnnotation)) public Object around(ProceedingJoinPoint joinPoint, LogExecution logAnnotation) throws Throwable { // 前记录开始时间 long start System.currentTimeMillis(); String methodName joinPoint.getSignature().getName(); log.info(开始执行方法{}说明{}, methodName, logAnnotation.value()); try { // 执行原方法 Object result joinPoint.proceed(); // 后记录结束时间和结果 long end System.currentTimeMillis(); log.info(方法执行完成{}耗时{}ms结果{}, methodName, end - start, result); return result; } catch (Exception e) { log.error(方法执行异常{}, methodName, e); throw e; } } }业务输出Service public class UserService { LogExecution(查询用户信息) public User getUserById(Long id) { // 业务逻辑 return userDao.findById(id); } }连接流程1.自定义注解类Target(ElementType.METHOD) // 只能标在方法上 Retention(RetentionPolicy.RUNTIME) // 运行时保留 public interface RepeatExecuteLimit { String name(); String[] keys(); int durationTime() default 5; String message() default 请勿重复提交; }切面类Aspect // 标记这是一个切面类 Component // 交给 Spring 容器管理 public class RepeatExecuteLimitAspect { Around(annotation(repeatLimit)) // 绑定注解 public Object around(ProceedingJoinPoint joinPoint, RepeatExecuteLimit repeatLimit) throws Throwable { // 前置逻辑 System.out.println(【切面】开始检查重复提交); // 执行原方法 Object result joinPoint.proceed(); // 后置逻辑 System.out.println(【切面】释放锁); return result; } }目标类Service // 交给 Spring 容器管理 public class OrderService { RepeatExecuteLimit(name submitOrder, keys {#userId}) public String submitOrder(Long userId, String orderId) { System.out.println(【业务】执行下单逻辑); return 订单提交成功; } }Spring AOP 自定义注解防重复提交的完整执行流程第一阶段项目启动时Spring 容器初始化第 1 步Spring Boot 启动开始扫描所有类。第 2 步扫描到Component注解的类发现RepeatExecuteLimitAspect切面类和OrderService目标类将它们都注册成 Spring 容器中的 Bean。第 3 步Spring 检测到RepeatExecuteLimitAspect类上有Aspect注解把它标记为切面类并解析类内部的所有通知方法。第 4 步解析到Around(annotation(repeatLimit))这个环绕通知Spring 理解到这个切面要拦截所有标注了RepeatExecuteLimit注解的方法并且会把注解对象作为参数传给切面方法。第 5 步Spring 开始检查容器中所有已经注册的 Bean看哪些 Bean 的方法上标注了RepeatExecuteLimit注解。发现OrderService的submitOrder方法上有这个注解判定匹配成功。第 6 步Spring 决定为OrderService这个 Bean 生成代理对象。根据OrderService是否实现了接口选择使用 JDK 动态代理或 CGLIB 代理。第 7 步代理对象创建完成它内部持有目标对象原始的OrderService和切面逻辑RepeatExecuteLimitAspect的around方法。第 8 步Spring 把容器中原本存放的原始OrderService对象替换成代理对象。从此容器里放的是代理对象原始对象被隐藏起来。第二阶段运行时用户发起请求第 1 步用户通过 Controller 调用orderService.submitOrder(123L, ORD001)。第 2 步Spring 从容器中取出orderService但实际上取出的是代理对象不是原始对象。第 3 步代理对象的invoke方法被触发它开始查找所有匹配当前方法的切面通知。第 4 步找到RepeatExecuteLimitAspect中的around方法准备执行环绕通知。第 5 步Spring 将目标方法上的RepeatExecuteLimit注解对象解析出来转换成RepeatExecuteLimit类型的实例连同ProceedingJoinPoint一起作为参数传入切面的around方法。第 6 步进入around方法执行前置切面逻辑读取注解中的name、keys、durationTime等属性根据这些信息生成 Redis 的锁 Key然后调用 Redis 的SETNX命令尝试加锁。第 7 步如果加锁失败说明已经有相同请求在执行直接抛出异常返回请勿重复提交的提示给用户整个过程结束原始业务方法根本不会执行。第 8 步如果加锁成功继续执行调用joinPoint.proceed()方法。第 9 步proceed()将控制权转交给原始的目标对象真正执行OrderService中的submitOrder业务方法执行下单逻辑返回结果。第 10 步业务方法执行完毕返回值回到切面的around方法中。第 11 步执行后置切面逻辑进入finally代码块删除 Redis 中的锁释放资源。第 12 步around方法将业务方法的结果返回给代理对象。第 13 步代理对象将结果返回给调用方Controller用户收到响应。核心要点总结三个类连接的关键自定义注解类定义标记规则切面类通过Around(annotation(参数名))建立匹配规则并接收注解实例目标类通过标注注解来表明哪些方法需要被增强。Spring 的连接机制容器启动时扫描所有 Bean 和切面匹配成功后为目标 Bean 生成代理对象并替换容器中的原对象。代理执行过程调用时先走代理代理执行切面的前置逻辑然后通过proceed()放行到原始方法最后执行切面的后置逻辑返回结果。代理和原始对象的关系容器里放的是代理对象代理对象内部持有原始对象的引用原始对象从未直接暴露给调用方。

相关新闻

别让论文降重服务坑了你:如何识别不可靠的文本改写服务

别让论文降重服务坑了你:如何识别不可靠的文本改写服务

论文降重服务避坑指南:如何看穿不靠谱的文本改写 写毕业论文时,降重和文本改写服务常被同学们当作“救命稻草”。但这类服务质量参差不齐,稍不留神,改完的论文不仅没达标,反而可能埋下更多隐患。结合我自己的踩坑经历…

2026/10/25 4:53:06 阅读更多 →
LabVIEW温度采集程序开发全解析:从硬件连接到TDMS存储

LabVIEW温度采集程序开发全解析:从硬件连接到TDMS存储

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 11:35:37 阅读更多 →
基于51单片机与nRF24L01的无线排队叫号系统设计与实现

基于51单片机与nRF24L01的无线排队叫号系统设计与实现

简介:本资源是一套完整的基于51单片机的无线医院排队叫号系统设计实现方案,面向嵌入式初学者、课程设计学生及电子类实训教师,解决传统人工叫号效率低、易出错、缺乏可视化与语音提示的问题。系统采用双板架构:取号板(…

2026/10/25 10:21:09 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/13 5:13:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/13 5:13:25 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/13 5:13:25 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/13 22:08:41 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/12 18:22:25 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/13 5:13:25 阅读更多 →

日新闻

周新闻

月新闻