SpringBoot实战:接口防重复提交的最佳实践
在前端网络抖动、用户手抖连击或者微服务重试机制下同一个接口可能在极短时间内被多次调用。如果接口不是幂等的如支付扣款、创建订单就会导致数据错乱。本文将深入讲解如何利用 Redis AOP 自定义注解 构建一套优雅的防重复提交方案。一、 什么是接口幂等性幂等性 (Idempotency)是指任意多次执行所产生的影响均与一次执行的影响相同。在 Web 开发中常见的非幂等场景包括用户提交订单时连点两次“支付”导致扣款两次。前端网络超时重试导致插入两条相同的记录。消息队列MQ消费者重试导致同一条消息被处理多次。为了保证系统的数据一致性我们需要对关键接口进行防重复提交处理。二、 常见解决方案对比方案原理优点缺点适用场景数据库唯一索引利用唯一约束拦截重复插入最底层兜底保障仅适用于插入场景无法拦截所有业务逻辑所有涉及唯一数据的场景Token 机制提交前请求 Token提交时校验并删除通用性强逻辑清晰需要前端配合多了一步交互表单提交、关键操作分布式锁基于请求参数或用户 ID 加锁实现简单无需前端改代码锁粒度控制复杂容易误伤正常请求简单防重非强一致性要求状态机控制通过记录状态流转如订单状态业务层面防重侵入业务代码订单、审批流本文重点实现Token 机制 AOP Redis这是目前企业中最通用、体验最好的方案。三、 核心思路Token 机制流程如下用户进入页面或点击提交前前端调用/api/token获取一个唯一的 Token。后端将 Token 存入 Redis设置过期时间如 5 分钟。前端在提交业务数据时将 Token 放在 Header 中。后端通过 AOP 拦截请求检查 Redis 中是否存在该 Token存在删除 Token放行请求。不存在说明是重复请求或 Token 已失效拒绝请求。为什么先存后删利用 Redis 的del操作是原子性的结合setnx(Set if Not Exists) 可以保证只有一个请求能成功拿到 Token。四、 实战编码1. 定义自定义注解我们希望防重逻辑对业务代码无侵入只需加一个注解即可。Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)DocumentedpublicinterfaceNoRepeatSubmit{/** * 锁过期时间默认 5 秒 */longexpireTime()default5;/** * 提示信息 */Stringmessage()default请勿重复提交;}2. Redis 工具类封装我们需要利用 Redis 的原子操作。这里假设你已经集成了RedisTemplate。ComponentpublicclassRedisUtil{AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalStringPREFIXno_repeat_submit:;/** * 尝试获取锁 (对应 Redis SETNX) * param key Key * param expireTime 过期时间 (秒) * return 如果 key 不存在则设置成功返回 true否则返回 false */publicbooleantryLock(Stringkey,longexpireTime){BooleansuccessredisTemplate.opsForValue().setIfAbsent(PREFIXkey,1,expireTime,TimeUnit.SECONDS);returnBoolean.TRUE.equals(success);}/** * 删除锁 (验证通过后删除) * 注意Token 机制通常是获取到就删除或者验证通过就删除。 * 如果是分布式锁模式则不需要这步依靠过期时间自动释放。 * 本方案采用 Token 模式请求来时检查是否有有则删除并放行。 */publicbooleanreleaseLock(Stringkey){// 这里为了简化直接删除。实际 Token 机制中tryLock 返回 true 即代表获取成功。// 如果是“检查并删除”的原子操作需要 Lua 脚本。// 但 Token 模式通常是前端拿 token - Redis set token - 提交带 token - 后端 check and delete token// 下面 AOP 中我们将演示“检查并删除”的逻辑returnBoolean.TRUE.equals(redisTemplate.delete(PREFIXkey));}}3. 编写 AOP 切面这是最核心的部分。Slf4jAspectComponentpublicclassNoRepeatSubmitAspect{AutowiredprivateRedisUtilredisUtil;Pointcut(annotation(com.example.annotation.NoRepeatSubmit))publicvoidpointcut(){}Around(pointcut())publicObjectdoAround(ProceedingJoinPointjoinPoint)throwsThrowable{// 1. 获取注解信息MethodSignaturesignature(MethodSignature)joinPoint.getSignature();Methodmethodsignature.getMethod();NoRepeatSubmitannotationmethod.getAnnotation(NoRepeatSubmit.class);// 2. 生成 Key (策略User ID 请求 URI 参数 Hash)// 这样防止不同接口之间冲突也防止同一接口不同参数冲突HttpServletRequestrequestgetRequest();StringuserIdgetCurrentUserId();// 假设从 SecurityContext 获取Stringurirequest.getRequestURI();StringparamsHashgetParamsHash(joinPoint.getArgs());StringlockKeyuserId:uri:paramsHash;// 3. 检查并删除 Token (原子操作)// 注意RedisTemplate 默认没有“检查并删除”的原子方法建议使用 Lua 脚本// 这里为了演示清晰使用简单的 exists delete实际高并发推荐 Luaif(redisUtil.releaseLock(lockKey)){log.info(重复提交校验通过: key{},lockKey);returnjoinPoint.proceed();}else{log.warn(重复提交拦截: key{},lockKey);thrownewBizException(400,annotation.message());}}// ... 辅助方法getRequest(), getCurrentUserId(), getParamsHash()// getParamsHash 可以通过计算 args 的 MD5 或 JSON HashCode 实现}修正Token 模式 vs 分布式锁模式Token 模式前端先请求/token后端redis.set(token, 1)。提交时后端redis.del(token)。如果删除成功返回 1说明是第一次提交如果删除失败返回 0说明是重复提交。这种方式更精准不会误杀并发请求。AOP 锁模式如上文 AOP 代码所示直接拦截。如果两个请求几乎同时到达del操作可能产生竞争。为了严谨AOP 防重提交通常采用 Redis Lua 脚本保证 “Get and Delete” 的原子性或者使用Token 模式。4. 进阶基于 Lua 脚本的原子删除为了在 AOP 中安全地实现“检查并删除”我们使用 Lua 脚本。-- Lua script: if key exists, delete it and return 1, else return 0ifredis.call(exists,KEYS[1])1thenreturnredis.call(del,KEYS[1])elsereturn0end在 Java 中封装publicbooleancheckAndDelete(Stringkey){Stringscriptif redis.call(exists, KEYS[1]) 1 then return redis.call(del, KEYS[1]) else return 0 end;DefaultRedisScriptLongredisScriptnewDefaultRedisScript(script,Long.class);LongresultredisTemplate.execute(redisScript,Collections.singletonList(PREFIXkey));returnresult!nullresult0;}将 AOP 中的判断逻辑替换为if (redisUtil.checkAndDelete(lockKey))即可。五、 方案落地总结前端配合如果是 Token 模式需要前端在进入页面时请求 Token 并缓存在变量中提交时携带。Key 的生成Key 必须包含用户标识防止 A 用户提交的 Token 被 B 用户复用。过期时间一定要设置过期时间防止 Redis 内存泄漏。粒度控制如果希望“同一用户同一接口不管参数如何都不能并发”Key 中可以去掉参数 Hash如果希望“不同参数可以并发”则必须带上参数 Hash。六、 常见坑点误杀正常请求如果 AOP 中使用简单的lock(key)(如setnxexpire)当请求处理时间超过过期时间锁自动释放第二个请求就会进来。这适合短时间防抖但不适合 Token 模式。Token 模式必须是“一次性的”。事务问题如果方法内有TransactionalAOP 的执行顺序很重要。确保防重校验在事务开启之前执行默认 AOP 顺序符合。分布式环境必须使用 Redis 或 ZooKeeper 等共享存储不能使用ConcurrentHashMap本地锁。七、 总结通过自定义注解 AOP Redis我们将防重复提交的逻辑从业务代码中剥离实现了开箱即用的效果。业务开发人员只需在 Controller 方法上加一行NoRepeatSubmit即可轻松解决并发提交导致的数据脏乱问题极大地提升了系统的健壮性。 福利时间如果你正在备战面试或者想要学习其他知识给大家推荐一个宝藏知识库作者整理了一些列 Java 程序员需要掌握的核心知识有需要的自取不谢。知识库地址https://farerboy.com/

相关新闻

【AI游戏开发辅助终极指南】:20年资深引擎架构师亲授5大落地场景与避坑清单

【AI游戏开发辅助终极指南】:20年资深引擎架构师亲授5大落地场景与避坑清单

更多请点击: https://kaifayun.com 第一章:AI游戏开发辅助的核心价值与演进脉络 AI正从“工具增强”跃迁为“协同创作主体”,在游戏开发全生命周期中重构效率边界与创意范式。早期AI仅承担自动化测试或简单NPC行为树生成,而当前大…

2026/7/29 1:04:01阅读更多 →
SpringBoot实战:接口参数校验与优雅处理

SpringBoot实战:接口参数校验与优雅处理

接口参数校验是后端开发的第一道防线。与其在业务代码中写满 if (name null),不如使用 Spring Boot 提供的参数校验机制,让代码更简洁、逻辑更清晰。一、 为什么需要参数校验? 在早期的开发中,我们经常在 Service 层写大量的防御…

2026/7/29 1:04:01阅读更多 →
剪映AI配音黑科技曝光:绕过语音克隆限制的3种合法路径(含实操录屏)

剪映AI配音黑科技曝光:绕过语音克隆限制的3种合法路径(含实操录屏)

更多请点击: https://intelliparadigm.com 第一章:剪映AI配音黑科技曝光:绕过语音克隆限制的3种合法路径(含实操录屏) 剪映PC端自v4.0起对AI配音功能实施了严格的语音克隆权限管控,仅限认证企业账号调用“…

2026/7/29 1:04:01阅读更多 →
Arduino PWM电机调速实战:从超声波测距到红外遥控的智能风扇设计

Arduino PWM电机调速实战:从超声波测距到红外遥控的智能风扇设计

1. 项目缘起:从“傻转”到“智控”的进化 如果你玩过Arduino,大概率做过让风扇转起来的实验。接上电机驱动模块,给个高电平,风扇呼呼转,成就感是有的,但总觉得少了点什么。没错,少的就是“智能”…

2026/7/29 4:45:12阅读更多 →
一个公开的存储桶,让我摸进了公司的内网:云存储安全攻防实战

一个公开的存储桶,让我摸进了公司的内网:云存储安全攻防实战

一个公开的存储桶,让我摸进了公司的内网:云存储安全攻防实战 引言:从一杯咖啡开始的“意外之旅”那是一个普通的周五下午,我正在办公室摸鱼刷推特,突然看到一条关于云存储桶暴露的推文。出于技术好奇心,我随…

2026/7/29 4:45:12阅读更多 →
免费开源AMD Ryzen调试工具SMUDebugTool:如何从硬件小白变身高阶玩家

免费开源AMD Ryzen调试工具SMUDebugTool:如何从硬件小白变身高阶玩家

免费开源AMD Ryzen调试工具SMUDebugTool:如何从硬件小白变身高阶玩家 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地…

2026/7/29 4:45:12阅读更多 →
2026实盘交易数据底座:海量时序数据存储与AI推理延迟优化实践

2026实盘交易数据底座:海量时序数据存储与AI推理延迟优化实践

引言:实盘系统的"隐形瓶颈"——数据与推理延迟 2026年,随着A股程序化交易监管新规的全面落地,实盘交易系统的竞争已从"策略Alpha"转向"工程基础设施"。许多量化团队在回测中表现优异的深度学习策略&#xff0c…

2026/7/29 4:45:12阅读更多 →
RS422 ABYZ信号连接DB9接口:接线方案、调试与硬件设计全解析

RS422 ABYZ信号连接DB9接口:接线方案、调试与硬件设计全解析

1. 项目缘起:从一堆线缆和接口开始的困惑最近在调试一个工业控制设备,手头拿到一块主控板,上面赫然印着“RS422”的接口标识,旁边清晰地标注着A、B、Y、Z四个信号引脚。而另一端,需要连接上位机的,是一个标…

2026/7/29 4:45:12阅读更多 →
MyBatis动态SQL安全实践:${}与#{}的深度解析与SQL注入防御

MyBatis动态SQL安全实践:${}与#{}的深度解析与SQL注入防御

1. 项目概述:从一道课后习题到企业级安全实践最近在辅导团队新人学习Java EE,特别是MyBatis框架时,总会遇到第三章关于动态SQL的课后习题。这些习题看似基础,但往往藏着许多新手乃至有一定经验的开发者都容易忽略的“坑”。尤其是…

2026/7/29 4:43:12阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“! 在构建复杂的 Agent 系统时,我们经常会遇到这样的场景:Agent 正在执行一个多步骤的任务,比如“下单购买商品”,但执行到一半时,我们…

2026/7/29 0:01:46阅读更多 →
自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…

2026/7/29 0:01:46阅读更多 →
【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:01:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/29 4:31:51阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →