正如本文标题所言,今天我们来聊一聊在Java应用系统中如何防止接口重复提交;简单地讲,这其实就是“重复提交”的话题,本文将从以下几个部分展开介绍:“重复提交”简介与造成的后果

“防止接口重复提交”的实现思路

“防止接口重复提交”的代码实战

1、“重复提交”简介与造成的后果

对于“重复提交”,想必各位小伙伴都知晓它的意思,简单的理解,它指的是前端用户在间隔很短的时间周期内对同一个请求URL发起请求,导致前端开发者在很短的时间周期内将同一份数据(请求体)提交到后端相同的接口 多次,最终数据库出现多条主键ID不一样而其他业务数据几乎一毛一样的记录;

仔细研究上述整个过程,会发现如果发起的多次请求的时间间隔足够短,即时间趋向于无穷小 时,其过程可以归为“多线程并发导致并发安全”的问题范畴;而对于“并发安全”的话题,debug早在此前自己录制的课程以及之前的文章中介绍过多次了,在此不再赘述;

上述在对“重复提交”的介绍中隐约也提及它所带来的的后果:

(1)数据库DB出现多条一毛一样的数据记录;

(2)如果重复发起的请求足够多、请求体容量足够大,很可能会给系统接口带来极大的压力,导致其出现“接口不稳定”、“DB负载过高”,严重点甚至可能会出现“系统宕机”的情况;

因此,我们需要在一些很可能会出现“重复提交”的后端接口中加入一些处理机制(附注:前端其实也需要配合一同处理的,其处理方式在本文就不做介绍了~);

2、“防止接口重复提交”的实现思路

值得一提的是,绝大部分情况下,只有POST/PUT/DELETE的请求方式才会出现“重复提交”的情况,而对于GET请求方式,只要不是出现人为的意外情况,那么它就具有“幂等性”,谈不上“重复提交”现象的出现,因此,在实际项目中,出现“重复提交”现象比较多的一般是POST请求方式;

而在实际项目开发中,“防止接口重复提交”的实现方式有两类,一类是纯粹的针对请求链接URL的,即防止对同一个URL发起多次请求:此种方式明显粒度过大,容易误伤友军;另一类是针对请求链接URL + 请求体 的,这种方式可以说是比较人性化而且也是比较合理的,而我们在后面要介绍的实现方式正是基于此进行实战的;

为了便于小伙伴理解,接下来我们以“用户在前端提交注册信息”为例,介绍“如何防止接口重复提交”的实现思路,如下图所示为整体的实现思路:

从该图中可以得知,如果当前提交的请求URL已经存在于缓存中,且 当前提交的请求体 跟 缓存中该URL对应的请求体一毛一样 且 当前请求URL的时间戳跟上次相同请求URL的时间戳 间隔在8s 内,即代表当前请求属于 “重复提交”;如果这其中有一个条件不成立,则意味着当前请求很有可能是第一次请求,或者已经过了8s时间间隔的 第N次请求了,不属于“重复提交”了。

3、“防止接口重复提交”的代码实战

照着这个思路,接下来我们将采用实际的代码进行实战,其中涉及到的技术:Spring Boot2.0 + 自定义注解 + 拦截器 + 本地缓存(也可以分布式缓存);

(1)首先,需要自定义一个用于加在需要“防止重复提交”的请求方法上 的注解RepeatSubmit,该注解的定义代码很简单,就是一个常规的注解定义,如下代码所示:

之后,是直接创建一个新的控制器SubmitController,并在其中创建一请求方法,用于处理前端用户提交的注册信息 请求,如下代码所示:

其中,RegisterDto 为自定义的实体类,代码定义如下所示:

(2)将注解加上去之后,接下来需要自定义一个拦截器RepeatSubmitInterceptor,用于拦截并获取 加了上述这个注解的所有请求方法的相关信息,包括其请求URL和请求体数据,其核心代码如下所示:

在这里我们将其定义为抽象类,并自定义一个抽象方法:“判断当前请求是否为重复提交isRepeatSubmit()”,之所以这样做,是因为“判断是否重复提交”可以有多种实现方式,而每种实现方式可以通过继承该抽象类 并 实现该抽象方法 从而将其区分开来,某种程度降低了耦合性(面向接口/抽象类编程);如下代码所示为该抽象类的其中一种实现方式:

该代码虽然看起来有点多,但是仔细研读,会发现其实这些代码 就是笔者在上文中贴出的实现流程图 的具体实现,可以说是将理论知识进行真正的落地实现;

在这里再重复赘述一下,其整体的实现思路为:获取当前请求的URL作为键Key,暂且标记为:A1,其取值为映射Map(Map里面的元素由:请求的链接url 、 请求体的数据、和 请求时的时间戳 三部分组成) 暂且标记为V1;从缓存中(本地缓存或者分布式缓存)查找Key=A1的值V2,如果V2和V1里的请求体数据一样 且 两次请求是在8s内,即代表当前请求是重复提交的,系统将拒绝执行后续的业务逻辑;否则可以继续往后面执行 “将用户信息插入到数据库中” 的业务逻辑;

(3)最后,需要将上述自定义的拦截器加入中系统全局配置中,如下所示:

运行项目,打开Postman,连续多番进行测试,如下几张图所示:

至此,我们已经采用实际的代码实战实现了“如何防止接口重复提交”的功能,值得一提的是,上述代码在实现过程中,其核心在于缓存组件的搭建;在“重复提交”这一业务场景中,它需要满足两个条件方可发挥作用:一个是可以用于缓存信息,即具有Key - Value的特性;另一个是可以对存储的数据设置过期时间;

在这里笔者采用的是google开发工具类中的CacheBuilder构建本地缓存组件的,感兴趣的小伙伴可以自行搜索相关资料;然而这种实现方式在集群多实例部署的情况下是有问题的,因为CacheBuilder只适用于单一架构体系,所以如果是多实例集群部署的情况,最好用Redis。

(1)文中涉及到的代码已经放在gitee上了,访问链接如下所示,别忘了给个star哦:https://gitee.com/steadyjack/SpringBootTechnologyA。

(2)期间如何有任何问题都可以私信debug。

(3)麻烦继续关注“程序员实战基地”,您的关注和转发 就是 debug勤劳写技术文的动力!!!

java 如何防止重复提交_干货实战~Java如何防止接口重复提交相关推荐

  1. java线程池饱和策略_干货:Java几种线程池的分析和使用。

    原标题:干货:Java几种线程池的分析和使用. 利用线程池的优势: 1.降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 2.提高响应速度.当任务到达时,任务可以不需要等到线程创建 ...

  2. java unsafe获取指针_【实战Java高并发程序设计 1】Java中的指针:Unsafe类

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

  3. java正则截取xml节点_实例讲述Java使用正则表达式截取重复出现的XML字符串功能...

    Java使用正则表达式截取重复出现的XML字符串功能示例 本文实例讲述了Java使用正则表达式截取重复出现的XML字符串功能.分享给大家供大家参考,具体如下: public static void m ...

  4. 实战java虚拟机 百度云_《实战JAVA虚拟机 JVM故障诊断与性能优化》pdf百度云下载...

    内容简介· · · · · · 随着越来越多的第三方语言(Groovy.Scala.JRuby等)在Java虚拟机上运行,Java也俨然成为了一个充满活力的生态圈.<实战Java虚拟机--JVM ...

  5. java mysql 自动提交_详解MySQL与Spring的自动提交(autocommit)

    1 MySQL的autocommit设置 MySQL默认是开启自动提交的,即每一条DML(增删改)语句都会被作为一个单独的事务进行隐式提交.如果修改为关闭状态,则执行DML语句之后要手动提交 才能生效 ...

  6. java查找链表中间元素_如何通过Java单次查找链表的中间元素

    java查找链表中间元素 您如何一次找到LinkedList的中间元素是一个编程问题,在电话采访中经常问Java和非Java程序员. 这个问题类似于检查回文或 计算阶乘 ,有时Interviewer还 ...

  7. java 线程中创建线程_如何在Java 8中创建线程安全的ConcurrentHashSet?

    java 线程中创建线程 在JDK 8之前,还没有办法在Java中创建大型的线程安全的ConcurrentHashSet. java.util.concurrent包甚至没有一个名为Concurren ...

  8. java面试没有全部答对_十道java基础面试题,你能保证全答对吗?

    java初级开发面试中经常被问到的问题有好多,下面总结一下常见的问题,先给问题,大家思考一下再看答案. 1.JDK 和 JRE 有什么区别?2.== 和 equals 有什么区别?3.说说final在 ...

  9. java list for循环遍历_详解Java中list,set,map的遍历与增强for循环

    详解Java中list,set,map的遍历与增强for循环 Java集合类可分为三大块,分别是从Collection接口延伸出的List.Set和以键值对形式作存储的Map类型集合. 关于增强for ...

  10. java字符串常量存哪里_浅谈JAVA中字符串常量的储存位置

    在讲述这些之前我们需要一些预备知识: Java的内存结构我们可以通过两个方面去看待它. 从该角度看的话Java内存结构包含以下部分:该部分内容可以结合:JVM简介(更加详细深入的介绍) 1.栈区:由编 ...

最新文章

  1. failed to load external entity file:/C:/Users/fmm/.AndroidStudio3.4/config/options/updates.xml
  2. 加密解密php,PHP实现的加密解密处理类
  3. [转载] Tmux 速成教程:技巧和调整
  4. .net framework 注册到IIS上
  5. Multiple Object Tracking:多目标跟踪综述
  6. Dataset之HiggsBoson:Higgs Boson(Kaggle竞赛)数据集的简介、下载、案例应用之详细攻略
  7. 第八届 蓝桥杯 承压计算
  8. mysql中group by 的用法解析
  9. 用t430搭建虚拟服务器教程,T430完美使用VmWare
  10. 打印异常堆栈_定位生产问题时,异常堆栈莫名丢了,何解?
  11. 移动应用程序框架Kendo UI Mobile发布R2 2016 SP2
  12. JavaScript中Ajax
  13. 20届美团提前批面经
  14. 如何用html绘制图表动画,10个经典实用的HTML5图表动画应用
  15. java poi 设置标题_java POI操作word2010简单实现多级标题结构
  16. destoon使用教程之经典调用方法汇总
  17. 机械光开关 MEMS光开关
  18. 一连发布三个版本,Boot要上天?
  19. Socket+MFC的聊天室
  20. FreeRTOSMini

热门文章

  1. 对于 ACM程序设计选修课的感想
  2. CS144——Lab0——networking warmup
  3. 使用vue扫描扫描仪图像
  4. 谷歌seo外链发布50+个网站平台分享(e6zzseo)
  5. 【埋点体系】(一)-埋点的理解
  6. 抽基类与PullToRefreshListView
  7. Migration中的Collation Confliction
  8. 一生必看的经典电影(转载)
  9. Echarts地图深入+散点
  10. 视觉SLAM十四讲CH9代码解析及课后习题详解