目录
一、核心方案选型(生产首选)
二、实战实现(完整可复制)
1. 依赖(Maven)
2. 定义防重提交注解(核心)
3. 防重 Key 生成策略(生产级)
4. AOP 切面实现(核心防重逻辑)
5. 接口使用(超级简单)
三、生产环境必须优化的 4 个关键点
1. Key 必须包含用户标识
2. 过期时间不能太长也不能太短
3. 必须使用 setIfAbsent(原子操作)
4. 接口必须保证幂等性
四、高并发场景进阶方案(生产必看)
方案 1:请求参数签名(更精准)
方案 2:Redisson 可重入锁(更稳定)
方案 3:全局异常统一返回
五、前端辅助防重(必须做)
六、最终生产防护体系(三层加固)
七、常见坑点(避坑指南)
总结
生产环境接口重复提交(用户连续点击、网络重试、接口重试)会导致:重复下单、重复扣款、重复创建数据、数据错乱等严重问题。
本文给你一套企业级、可直接落地、生产可用的防重提交实战方案,包含:原理 + 代码 + 配置 + 坑点。
一、核心方案选型(生产首选)
| 方案 | 适用场景 | 生产推荐度 |
|---|---|---|
| Redis + 分布式锁 | 微服务、集群、高并发 | ⭐⭐⭐⭐⭐ 首选 |
| 数据库唯一索引 | 强一致性、幂等兜底 | ⭐⭐⭐⭐ 必须配合 |
| 前端按钮禁用 | 基础防护 | ⭐⭐ 辅助 |
最终实战方案:前端防点 + Redis 分布式锁防重 + 数据库唯一索引兜底
二、实战实现(完整可复制)
1. 依赖(Maven)
<!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>2. 定义防重提交注解(核心)
import java.lang.annotation.*; import java.util.concurrent.TimeUnit; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RepeatSubmit { /** * 防重时间窗口(默认 2 秒) */ int expire() default 2; /** * 时间单位 */ TimeUnit timeUnit() default TimeUnit.SECONDS; /** * 提示信息 */ String message() default "操作过于频繁,请稍后再试"; }3. 防重 Key 生成策略(生产级)
Key 必须唯一,推荐组合:prefix:用户唯一标识:接口URI:请求参数签名
import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; public class RequestKeyGenerator { // 从请求头拿 token(你项目里的用户标识) public static String getUserId() { HttpServletRequest request = getRequest(); // 替换成你项目的用户ID获取方式 return request.getHeader("userId"); } public static HttpServletRequest getRequest() { RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); return ((ServletRequestAttributes) attributes).getRequest();