• 思路
  • Code
  • 多线程测试

  • 《Java 2019 超神之路》
  • 《Dubbo 实现原理与源码解析 —— 精品合集》
  • 《Spring 实现原理与源码解析 —— 精品合集》
  • 《MyBatis 实现原理与源码解析 —— 精品合集》
  • 《Spring MVC 实现原理与源码解析 —— 精品合集》
  • 《Spring Boot 实现原理与源码解析 —— 精品合集》
  • 《数据库实体设计合集》
  • 《Java 面试题 —— 精品合集》
  • 《Java 学习指南 —— 精品合集》

“在传统的web项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端。页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性。

上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些大了,本节的实现方案无需前端配合,纯后端处理。

思路

  1. 自定义注解 @NoRepeatSubmit 标记所有Controller中的提交请求
  2. 通过AOP 对所有标记了 @NoRepeatSubmit 的方法拦截
  3. 在业务方法执行前,获取当前用户的 token(或者JSessionId)+ 当前请求地址,作为一个唯一 KEY,去获取 Redis 分布式锁(如果此时并发获取,只有一个线程会成功获取锁)
  4. 业务方法执行后,释放锁

关于Redis 分布式锁

  • 不了解的同学戳这里 ==> Redis分布式锁的正确实现方式
  • 使用Redis 是为了在负载均衡部署,如果是单机的部署的项目可以使用一个线程安全的本地Cache 替代 Redis

Code

这里只贴出 AOP 类和测试类,完整代码见 ==> Gitee

@Aspect
@Component
public class RepeatSubmitAspect {private final static Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);@Autowiredprivate RedisLock redisLock;@Pointcut("@annotation(noRepeatSubmit)")public void pointCut(NoRepeatSubmit noRepeatSubmit) {}@Around("pointCut(noRepeatSubmit)")public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {int lockSeconds = noRepeatSubmit.lockTime();HttpServletRequest request = RequestUtils.getRequest();Assert.notNull(request, "request can not null");// 此处可以用token或者JSessionIdString token = request.getHeader("Authorization");String path = request.getServletPath();String key = getKey(token, path);String clientId = getClientId();boolean isSuccess = redisLock.tryLock(key, clientId, lockSeconds);if (isSuccess) {LOGGER.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);// 获取锁成功, 执行进程Object result;try {result = pjp.proceed();} finally {// 解锁redisLock.releaseLock(key, clientId);LOGGER.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);}return result;} else {// 获取锁失败,认为是重复提交的请求LOGGER.info("tryLock fail, key = [{}]", key);return new ResultBean(ResultBean.FAIL, "重复请求,请稍后再试", null);}}private String getKey(String token, String path) {return token + path;}private String getClientId() {return UUID.randomUUID().toString();}}

多线程测试

测试代码如下,模拟十个请求并发同时提交

@Component
public class RunTest implements ApplicationRunner {private static final Logger LOGGER = LoggerFactory.getLogger(RunTest.class);@Autowiredprivate RestTemplate restTemplate;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("执行多线程测试");String url="http://localhost:8000/submit";CountDownLatch countDownLatch = new CountDownLatch(1);ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0; i<10; i++){String userId = "userId" + i;HttpEntity request = buildRequest(userId);executorService.submit(() -> {try {countDownLatch.await();System.out.println("Thread:"+Thread.currentThread().getName()+", time:"+System.currentTimeMillis());ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);System.out.println("Thread:"+Thread.currentThread().getName() + "," + response.getBody());} catch (InterruptedException e) {e.printStackTrace();}});}countDownLatch.countDown();}private HttpEntity buildRequest(String userId) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("Authorization", "yourToken");Map<String, Object> body = new HashMap<>();body.put("userId", userId);return new HttpEntity<>(body, headers);}}

成功防止重复提交,控制台日志如下,可以看到十个线程的启动时间几乎同时发起,只有一个请求提交成功了

来源:http://jianshu.com/p/09860b74658e

  • 《Java 2019 超神之路》
  • 《Dubbo 实现原理与源码解析 —— 精品合集》
  • 《Spring 实现原理与源码解析 —— 精品合集》
  • 《MyBatis 实现原理与源码解析 —— 精品合集》
  • 《Spring MVC 实现原理与源码解析 —— 精品合集》
  • 《Spring Boot 实现原理与源码解析 —— 精品合集》
  • 《数据库实体设计合集》
  • 《Java 面试题 —— 精品合集》
  • 《Java 学习指南 —— 精品合集》

springboot防止表单重复提交_Spring Boot 使用 AOP 防止重复提交相关推荐

  1. 【一文学会文件上传】SpringBoot+form表单实现文件上传

    唠嗑部分 平时我们在项目过程中,往往会遇到这种情况,比如:我的用户应该有一个头像,那就涉及到文件上传,那么文件应该如何存储呢? 这就会有很多方式 1.最简单的就是存在服务器上,这就要考虑到服务器的磁盘 ...

  2. 用javascript写一个简单的登录表单,包含用户名、密码和手机号码。提交数据时验证用户名、密码和手机号输入格式。

    题目:写一个用于登录的表单,包含用户名.密码和手机号码.提交数据的时候,要求验证: 1. 用户名必须输入汉字: 2. 密码总共6位,前2位为字母,后4位为数字: 3. 手机号码符合中国大陆手机号码格式 ...

  3. springboot 订单重复提交_Spring Boot (一) 校验表单重复提交

    一.前言 在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据. 存在如上问题可以交给前端解决,判断多长时间内不能再次点 ...

  4. springboot防止表单重复提交

    springboot和vue使用时网络卡导致请求并发提交 数据请求做了去重处理,但是无法处理重复并发请求 实现代码 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown ...

  5. SpringBoot+Vue表单文件上传

    版权声明:本文首发 http://asing1elife.com ,转载请注明出处. https://blog.csdn.net/asing1elife/article/details/8281181 ...

  6. 表单验证[用户名、邮箱、密码、重复密码]

    <!DOCTYPE html> <html>     <head>         <meta charset="utf-8">   ...

  7. html表单提交后怎么发送邮箱,Dreamweaver中用表单制作了留言板,如何将内容提交后发到指定邮箱?...

    解决思路: 关键点是设置表单的action属性为"mailto:"+email地址. 具体步骤: 1.设置表单的action属性为接收邮件的email地址,并设置表单的MIME编码 ...

  8. php 表单 同步,Jquery点击按钮 异步和同步提交表单

    最近在开发一个jsp学生信息管理系统,由于刚刚接触jsp,遇到问题比较多,特此记录与大家分享. Jquery ajax提交表单到servlet示例 前台部分代码: 姓名 学号      ajax提交表 ...

  9. form表单下的button不设置type会自动提交表单

    有时html页面button点击会出现自动提交表单的情况. 后来查资料得知,form表单下的按钮在没有指定type类型的时候,button会有一个默认的type="submit" ...

  10. jq html 回车提交表单,jQuery事件之键盘事件(ctrl+Enter回车键提交表单等)

    键盘事件处理所有用户在键盘敲击的情况,不管在文本输入区域内部还是外部.键盘事件在不同的浏览器中作用的范围是不一样的,通常这种键盘事件可以作用于 Form元素,a标签元素,window ,documen ...

最新文章

  1. 我为什么辞去了热爱的 AI 工作?
  2. code first基础
  3. python修改linux网卡信息,linux下修改网卡接口名称
  4. MySQL The password hash doesn't have the expected format.
  5. slf4j介绍以及实现原理窥探
  6. Notification的学习,4.0前后的差别,和在设置声音的时候获取资源的uri方法
  7. 属性面板 脚本_如何在组态王中实现同类型设备公用操作面板的调用---干货
  8. CVPR 2021 | 姿态可控的语音驱动
  9. iOS应用商店审核指南
  10. 使用laypage分页
  11. everthing 打开历史记录功能
  12. 深度学习视频教程推荐
  13. App隐私合规注意事项和相关材料
  14. Flutter ListView ListView.build ListView.separated
  15. MNIST手写数字数据集读取方法
  16. python注释第一行_python第一行注释是什么意思?
  17. VScode快速创建HTML标签
  18. python版本切换 for Windows(Anaconda) Linux(pyenv)
  19. 史上最详细:word文档怎么在第三页插入页码,只要7步
  20. Textual Entailment(自然语言推理-文本蕴含) - AllenNLP

热门文章

  1. spring MVC 使用 modelAndView.setViewName(forward:*.action) 发送重定向
  2. 统计细菌基因组ORF
  3. opencv5-objdetect之级联分类器
  4. Shiro权限控制框架
  5. 安腾机器IA64 全自动网络安装Linux - elilo+kickstart
  6. centos SSH免密登录
  7. redis数据类型之String
  8. Activiti(Flowable)流程克隆(运行时实例克隆)解决方案 一种订单制造中分裂与合并的核心技术实践...
  9. Jni 返回jstring字符串
  10. python购物车----运维开发初学