当前位置: 首页 > news >正文

java防止表单重复提交的注解@RepeatSubmit

代码解释

@RepeatSubmit
是一个自定义注解,通常用于防止表单重复提交。这个注解可以应用于控制器方法上,以确保同一个请求在一定时间内不会被多次提交。以下是一些常见的参数和用法:
value: 注解的名称或描述。 interval: 两次请求之间的最小间隔时间(单位通常是毫秒)。 message:
当检测到重复提交时返回的提示信息。

示例代码

假设有一个 @RepeatSubmit 注解的定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {String value() default "";int interval() default 3000; // 默认3秒String message() default "请勿重复提交";
}

使用示例 在控制器方法中使用 @RepeatSubmit 注解:

@RestController
public class UserController {@PostMapping("/submitForm")@RepeatSubmit(interval = 5000, message = "请等待5秒后再提交")public ResponseEntity<String> submitForm(@RequestBody FormData formData) {// 处理表单提交逻辑return ResponseEntity.ok("表单提交成功");}
}

控制流图

以下是 @RepeatSubmit 注解的控制流图,展示了其工作原理:

flowchart TD
A[开始] --> B[接收请求]
B --> C{检查是否重复提交}
C -->|是| D[返回重复提交提示信息]
C -->|否| E[处理请求]
E --> F[返回成功响应]
F --> G[结束]

说明

A: 开始处理请求。
B: 接收到客户端的请求。
C: 检查当前请求是否与前一次请求的时间间隔小于设定的 interval。
D: 如果检测到重复提交,返回提示信息(如 “请等待5秒后再提交”)。
E: 如果没有检测到重复提交,继续处理请求。
F:请求处理成功后,返回成功响应。
G: 结束请求处理过程。

使用的设计模式

@RepeatSubmit 注解通常结合 AOP(面向切面编程) 和 拦截器模式 来实现防止表单重复提交的功能。

设计模式解析

AOP(面向切面编程):
目的: 将横切关注点(如日志记录、事务管理、安全性等)从业务逻辑中分离出来,提高代码的模块化和可维护性。
实现: 使用 Spring AOP 或其他 AOP 框架,通过切面(Aspect)来拦截方法调用,执行额外的逻辑(如检查重复提交)。

拦截器模式:

目的: 在请求到达目标方法之前或之后执行特定的逻辑。
实现: 在 Spring 中,可以通过 HandlerInterceptor 或 MethodInterceptor 来实现拦截器,拦截请求并执行预处理或后处理逻辑

定义注解:

   @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface RepeatSubmit {String value() default "";int interval() default 3000; // 默认3秒String message() default "请勿重复提交";}

创建切面:

   @Aspect@Componentpublic class RepeatSubmitAspect {@Around("@annotation(repeatSubmit)")public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {// 获取方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 获取请求上下文HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 获取注解参数int interval = repeatSubmit.interval();String message = repeatSubmit.message();// 从 session 中获取上次请求的时间戳Long lastRequestTime = (Long) request.getSession().getAttribute(method.getName());// 检查是否重复提交if (lastRequestTime != null && System.currentTimeMillis() - lastRequestTime < interval) {throw new RuntimeException(message);}// 记录当前请求的时间戳request.getSession().setAttribute(method.getName(), System.currentTimeMillis());// 继续执行目标方法return joinPoint.proceed();}}

在控制器方法中使用注解

   @RestControllerpublic class UserController {@PostMapping("/submitForm")@RepeatSubmit(interval = 5000, message = "请等待5秒后再提交")public ResponseEntity<String> submitForm(@RequestBody FormData formData) {// 处理表单提交逻辑return ResponseEntity.ok("表单提交成功");}}

使用 @RepeatSubmit需要注意什么

使用 @RepeatSubmit 注解来防止表单重复提交时,需要注意以下几个方面:
1. 注解参数配置

interval:
设置合理的间隔时间。过短的间隔时间可能导致用户频繁遇到重复提交的提示,影响用户体验;过长的间隔时间可能无法有效防止快速连续提交。
message: 提供明确的提示信息,告知用户为什么请求被拒绝,帮助用户理解并采取正确的操作。

2. 并发处理

线程安全: 在高并发环境下,确保时间戳的读取和写入操作是线程安全的。可以使用 ConcurrentHashMap 或 AtomicLong
等线程安全的数据结构来存储时间戳。 分布式环境: 如果应用部署在多个服务器上,需要考虑如何在分布式环境中共享时间戳信息。可以使用 Redis
等分布式缓存来存储时间戳。

3. 用户体验

前端提示: 在前端页面上添加防重复提交的机制,如禁用提交按钮、显示加载动画等,减少用户误操作的可能性。 错误处理:
提供友好的错误处理机制,当检测到重复提交时,返回清晰的错误信息,并引导用户重新尝试或联系支持人员。

4. 性能考虑

性能开销: 防重复提交的检查会增加一定的性能开销,特别是在高并发场景下。确保这些检查不会成为系统性能的瓶颈。 缓存策略:
使用缓存来存储时间戳信息,减少对数据库或会话的频繁访问,提高性能。

5. 安全性

会话管理: 确保会话管理的安全性,防止会话劫持等攻击。 时间戳验证: 验证时间戳的有效性和合法性,防止恶意用户篡改时间戳。

6. 日志记录

日志记录: 记录每次请求的时间戳和处理结果,便于后续的审计和问题排查。

示例代码
以下是一个更完善的 @RepeatSubmit 注解和切面实现,考虑了上述注意事项:
定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {String value() default "";int interval() default 3000; // 默认3秒String message() default "请勿重复提交";
}
// 创建切面
@Aspect
@Component
public class RepeatSubmitAspect {@Autowiredprivate RedisTemplate<String, Long> redisTemplate;@Around("@annotation(repeatSubmit)")public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {// 获取方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 获取请求上下文HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 获取注解参数int interval = repeatSubmit.interval();String message = repeatSubmit.message();// 生成唯一的请求标识String key = method.getName() + ":" + request.getRemoteAddr();// 从 Redis 中获取上次请求的时间戳Long lastRequestTime = redisTemplate.opsForValue().get(key);// 检查是否重复提交if (lastRequestTime != null && System.currentTimeMillis() - lastRequestTime < interval) {throw new RuntimeException(message);}// 记录当前请求的时间戳redisTemplate.opsForValue().set(key, System.currentTimeMillis(), interval, TimeUnit.MILLISECONDS);// 继续执行目标方法return joinPoint.proceed();}
}
// 在控制器方法中使用注解
@RestController
public class UserController {@PostMapping("/submitForm")@RepeatSubmit(interval = 5000, message = "请等待5秒后再提交")public ResponseEntity<String> submitForm(@RequestBody FormData formData) {// 处理表单提交逻辑return ResponseEntity.ok("表单提交成功");}
}

控制流图
以下是 @RepeatSubmit 注解的控制流图,展示了其工作原理:

flowchart TDA[开始] --> B[接收请求]B --> C[生成唯一请求标识]C --> D[从 Redis 获取上次请求时间戳]D -->|存在且未过期| E[返回重复提交提示信息]D -->|不存在或已过期| F[记录当前请求时间戳]F --> G[继续执行目标方法]G --> H[返回成功响应]H --> I[结束]

说明
A: 开始处理请求。
B: 接收到客户端的请求。
C: 生成唯一的请求标识,通常包括方法名和客户端 IP 地址。
D: 从 Redis 中获取上次请求的时间戳。
E: 如果存在且未过期,返回重复提交提示信息。
F: 如果不存在或已过期,记录当前请求的时间戳。
G: 继续执行目标方法。
H: 请求处理成功后,返回成功响应。
I: 结束请求处理过程。

使用@RepeatSubmit会失效吗

使用 @RepeatSubmit 注解来防止表单重复提交时,确实可能会遇到一些情况下失效的问题。以下是一些常见的失效原因及解决方案:

> 1. 前端快速连续点击

原因: 用户在短时间内快速连续点击提交按钮,导致后端无法及时响应和处理。 解决方案: 前端禁用按钮:
在用户点击提交按钮后,立即禁用按钮,防止多次点击。 前端显示加载动画: 显示加载动画,告知用户请求正在处理中。

2. 网络延迟

原因: 网络延迟可能导致用户认为请求失败,从而再次提交。 解决方案: 前端超时提示: 设置合理的请求超时时间,并在超时后提示用户。
后端重试机制: 在后端实现重试机制,但需谨慎处理,避免无限重试。

3. 会话失效

原因: 如果使用会话(Session)来存储时间戳,会话可能因超时或服务器重启而失效。 解决方案: 使用分布式缓存: 使用 Redis
等分布式缓存来存储时间戳,确保在多服务器环境下也能正常工作。

4. 并发请求

原因: 在高并发环境下,多个请求可能同时到达,导致时间戳检查失效。 解决方案: 线程安全: 使用线程安全的数据结构(如
ConcurrentHashMap 或 AtomicLong)来存储时间戳。 分布式锁: 在分布式环境下,使用分布式锁(如 Redis
分布式锁)来确保时间戳的读取和写入操作是原子性的


5. 时间戳精度问题

原因: 时间戳的精度可能不够高,导致短时间内多次请求被视为同一请求。 解决方案: 提高时间戳精度:
使用更高精度的时间戳(如纳秒)来减少冲突。

6. 代码逻辑错误

原因: 切面或拦截器的逻辑错误可能导致 @RepeatSubmit 注解失效。 解决方案: 代码审查:
仔细审查切面或拦截器的代码,确保逻辑正确。 单元测试: 编写单元测试,覆盖各种边界情况,确保 @RepeatSubmit 注解按预期工作。

相关文章:

java防止表单重复提交的注解@RepeatSubmit

代码解释 RepeatSubmit 是一个自定义注解&#xff0c;通常用于防止表单重复提交。这个注解可以应用于控制器方法上&#xff0c;以确保同一个请求在一定时间内不会被多次提交。以下是一些常见的参数和用法&#xff1a; value: 注解的名称或描述。 interval: 两次请求之间的最小间…...

HTTP快速入门

HTTP报文结构 HTTP 协议主要由三大部分组成&#xff1a; ● 起始行&#xff08;start line&#xff09;&#xff1a;描述请求或响应的基本信息&#xff1b; ● 头部字段&#xff08;header&#xff09;&#xff1a;使用 key-value 形式更详细地说明报文&#xff1b; ● 消息正…...

Nacos简介

Nacos是一个开源的动态服务发现、配置管理和服务管理平台&#xff0c;由阿里巴巴集团开发并开源。它提供了服务注册与发现、配置管理、动态DNS服务、服务健康监测、权重和流量管理等核心特性&#xff0c;非常适合构建云原生应用和微服务架构。 Nacos的核心功能包括&#xff1a…...

基于深度学习的稳健的模型推理与不确定性建模

基于深度学习的稳健模型推理与不确定性建模&#xff0c;是现代AI系统中至关重要的研究方向。随着深度学习在各类应用中的成功&#xff0c;如何保证模型在面对未知或不确定性输入时仍能做出稳健的推理&#xff0c;并能够量化这种不确定性&#xff0c;成为关键问题。稳健性与不确…...

C语言 sizeof 的介绍,以及sizeof计算数组名、 数组首地址、数组的元素之间的区别

一、sizeof 介绍 sizeof 是 C 语言中的一个运算符&#xff0c;用于计算数据类型或变量在内存中占用的字节数。用于计算数据类型或变量所占的内存大小&#xff0c;以字节为单位。它可以在编译时计算其操作数的大小&#xff0c;并返回一个 size_t 类型的值。它可以帮助了解不同类…...

深入理解Oracle闪回技术

引言&#xff1a; Oracle 闪回&#xff08;Flashback&#xff09;是一组强大的功能&#xff0c;用于恢复数据库中的数据或对象到过去的某个时间点或状态&#xff0c;而无需进行传统的基于备份和恢复的操作。 Oracle 闪回的主要类型 1. 闪回查询&#xff08;Flashback Query&…...

Go 语言初探

Google 公司有一个传统,允许员工利用 20% 的工作时间开发自己的实验项目。2007 年 9月,UTF-8 的设计者之一 Rob Pike(罗布.皮克)在 Google 的分布式编译平台上进行 C++ 编译时,与同事 Robert Griesemer (罗布.格里泽默)在漫长的等待中讨论了编程语言面临的主要问题。他们一…...

使用ROS资源编排一键部署LNMP建站环境,手动整理教程

LNMP是目前主流的网站服务器架构之一&#xff0c;适合运行大型和高并发的网站应用&#xff0c;例如电子商务网站、社交网络、内容管理系统等。LNMP分别代表Linux、Nginx、MySQL和PHP。本文阿里云服务器网aliyunfuwuqi.com介绍如何使用阿里云资源编排服务&#xff08;ROS&#x…...

猎板PCB镍钯金工艺你了解多少?

PCB镍钯金工艺&#xff0c;也称为ENEPIG&#xff08;Electroless Nickel Electroless PALLADIum Gold&#xff09;工艺&#xff0c;是一种在PCB表面处理中使用的先进工艺。这种工艺通过在PCB线路板上形成一层镍钯合金层&#xff0c;有效地提高了线路板的耐氧化性、耐腐蚀性和可…...

热更新解决方案2 —— Lua语法相关知识点

概述 开发环境搭建 Lua语法 1.第一个Lua程序 2.变量 print("******变量*******"); --lua当中的简单变量类型 -- nil number string boolean -- lua 中所有的变量声明 都不需要声明变量类型 它会自动的判断类型 -- 类似C# 中的var --lua中的一个变量 可以随便赋值 ——…...

【c++ arx选项板】

static void xlArx_gmenu(void) {if (!g_pPaletteSetEx){g_pPaletteSetEx=CTunnelSectionPaletteSetEx::Instance(...

新时代下吉林省城乡流动人才就业问题及路径探析

摘要&#xff1a;新时代背景下&#xff0c;中国经济快速发展&#xff0c;城乡融合发展成为缩小城乡差距&#xff0c;推动共同富裕的重要方式。吉林省作为东北老工业基地&#xff0c;传统产业竞争优势减弱&#xff0c;城乡流动人才就业规模增加&#xff0c;并呈现“农村-城市”的…...

Go 1.19.4 命令调用、日志、包管理、反射-Day 17

1. 系统命令调用 所谓的命令调用&#xff0c;就是通过os&#xff0c;找到系统中编译好的可执行文件&#xff0c;然后加载到内存中&#xff0c;变成进程。 1.1 exec.LookPath&#xff08;寻找命令&#xff09; 作用&#xff1a; exec.LookPath 函数用于在系统的环境变量中搜索可…...

Unity 2d UI 实时跟随场景3d物体

2d UI 实时跟随场景3d物体位置&#xff0c;显示 3d 物体头顶信息&#xff0c;看起来像是场景中的3dUI&#xff0c;实质是2d UIusing System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using UnityEngine.UI; /// <summary>…...

全方面熟悉Maven项目管理工具(二)坐标、pom.xml文件的解读!

1. 坐标&#xff08;核心概念&#xff09; 1.1 数学中的坐标 使用 x、y、z 三个向量作为空间的坐标系&#xff0c;可以在空间中唯一的定位到一个点 1.2 Maven 中的坐标 1.2.1 向量说明&#xff1a; 使用三个向量在 Maven的仓库 中唯一的定位到一个 jar 包 groupId&#xf…...

php常用设计模式之单例模式

设计模式是我们日常开发中最常用的编程模式之一&#xff0c;也是面试中最高频的考点之一。通过合理运用设计模式&#xff0c;可以使代码结构更加清晰、易于维护。通过这篇文章 我也讲一下设计模式中的单例模式&#xff0c;了解下它的原理和适用场景。 单例模式 单例模式&…...

一文搞懂Android主题和样式

1. 概念与作用 1.1 定义与组成 Android Theme是用于定义应用程序或其部分的视觉和界面风格的一种资源。主题在Android中扮演着重要的角色&#xff0c;它们允许开发者统一和自定义应用的外观和感觉。一个主题定义了一组属性集合&#xff0c;这些属性可以是颜色、字体、控件样式…...

360与重庆科技大学战略携手,为数字中国建设输送实战人才

近日&#xff0c;360数字安全集团与重庆科技大学正式签订了战略合作协议&#xff0c;双方将围绕创新人才培养、科研技术攻关、专业实验室共建、车联网安全以及社会服务等多个维度展开深度合作&#xff0c;共同打造数字安全人才培养新高地&#xff0c;为数字重庆的建设与发展注入…...

基于异常合成的图像异常检测方法

基于异常合成的图像异常检测方法 基于异常合成思路实现图像异常检测的方法&#xff0c;它们的核心思路是&#xff1a; 试图通过合成异常样本穷尽所有可能出现的异常类型&#xff0c;从而将无监督的异常检测&#xff08;one class classification&#xff09;建模为一个全监督…...

机器学习方向在算法优化上有哪些创新点?

以下是机器学习算法优化方面的一些创新点: 一、优化算法自身的改进 随机梯度下降(SGD)的变体 Adagrad 传统的随机梯度下降算法使用固定的学习率,而Adagrad根据每个参数的历史梯度信息自适应地调整学习率。对于稀疏数据,它可以为不同的参数分配不同的学习率,使得频繁出现…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...