Java中可通过自定义注解@PreventDuplicateSubmit结合AOP与Redis实现防重复提交,核心是在方法执行前用SpEL解析唯一key并Redis SETNX加锁,超时时间内重复请求被拒绝。

Java 中可以通过自定义注解 + 拦截器(或 AOP)+ 缓存(如 Redis)实现防重复提交功能,核心思路是:在方法执行前校验请求是否已处理过,若存在则拒绝执行。
1. 定义自定义注解 @PreventDuplicateSubmit
声明一个运行时保留、可作用于方法的注解,支持配置超时时间(单位秒)和业务唯一键表达式(支持 SpEL):
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreventDuplicateSubmit {
long timeout() default 5; // 默认 5 秒内视为重复
String key() default ""; // SpEL 表达式,如 "#request.getHeader('X-Request-ID') 或 #user.id"
}
2. 实现拦截逻辑(推荐用 Spring AOP)
编写切面,在目标方法执行前生成唯一请求标识并尝试加锁(如 Redis SETNX),成功则放行,失败则抛出异常:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 从注解中提取 timeout 和 key 表达式
- 用 SpelExpressionParser 解析 key,结合方法参数计算实际缓存 key(如
duplicate:submit:userId:123) - 调用 Redis 的
set(key, "1", timeout, TimeUnit.SECONDS),设置带过期时间的占位值 - 若返回 false(key 已存在),说明该请求已在窗口期内提交过,抛出
DuplicateSubmitException
3. 在 Controller 方法上使用注解
只需在需要防护的接口方法上添加注解即可,例如:
立即学习“Java免费学习笔记(深入)”;
@PostMapping("/order")
@PreventDuplicateSubmit(timeout = 10, key = "#request.getHeader('X-Idempotency-Key')")
public Result createOrder(@RequestBody OrderDTO order, HttpServletRequest request) {
return orderService.create(order);
}
客户端需在请求头中携带 X-Idempotency-Key(如 UUID),服务端据此识别同一笔请求。
4. 注意事项与增强点
- key 表达式尽量包含用户身份、业务类型、关键参数,避免全局冲突
- Redis 连接必须高可用,建议设置 fallback(如本地 Caffeine 缓存兜底)
- 对 GET 请求通常不启用,因幂等性天然满足;重点保护 POST/PUT/DELETE
- 前端也应配合做按钮置灰、loading 状态,但服务端校验不可省略
- 异常需统一捕获并返回友好提示(如 HTTP 425 Too Early 或自定义错误码)

















