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

springboot aop实现接口防重复操作

一、前言
有时在项目开发中某些接口逻辑比较复杂,响应时间长,那么可能导致重复提交问题。

二、如何解决
1.先定义一个防重复提交的注解。

import java.lang.annotation.*;@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {/*** 防重复操作限时标记数值(存储redis限时标记数值)*/String value() default "value" ;/*** 防重复操作过期时间(借助redis实现限时控制)*/int expireSeconds() default 10;
}

2.编写防重复操作的AOP

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;@Slf4j
@Component
@Aspect
@Order(0)
public class NoRepeatSubmitAspect  {private static final String TOKENAuthorization = "Authorization";private static final String TOKENUSERNAME = "api-userName";private static final String PREVENT_DUPLICATION_PREFIX = "PREVENT_DUPLICATION_PREFIX:";@Autowiredprivate RedisService redisService;@Pointcut("@annotation(com.dp.aop.annotation.RepeatSubmit)")public void preventDuplication() {}@Around("preventDuplication()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();if (Objects.isNull(request)) {return joinPoint.proceed();}//获取执行方法Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();//获取防重复提交注解RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);//获取token以及方法标记,生成redisKeyString header = request.getHeader(TOKENAuthorization);String token = header == null ? "" : header;String requestHeader = request.getHeader(TOKENUSERNAME);String headerToken = requestHeader == null ? "" : requestHeader;token = token + headerToken;String url = request.getRequestURI();// 通过前缀 + url + token + 函数参数签名 来生成redis上的 keyString redisKey = PREVENT_DUPLICATION_PREFIX.concat(url).concat(token).concat(getMethodSign(method, joinPoint.getArgs()));RedisLock redisLock = null;try {try {redisLock = redisService.tryLock(redisKey, annotation.expireSeconds());} catch (Exception e) {log.error("tryLock error  ", e);throw new BizException(CommonMsgConstants.NoRepeatSubmitMsg);}return joinPoint.proceed();} catch (Throwable throwable) {log.error("throwable trace is ", throwable);throw new RuntimeException(throwable);} finally {if (Objects.nonNull(redisLock)) {redisLock.unlock();}}}/*** 生成方法标记:采用数字签名算法SHA1对方法签名字符串加签** @param method* @param args* @return*/private String getMethodSign(Method method, Object... args) {StringBuilder sb = new StringBuilder(method.toString());for (Object arg : args) {sb.append(toString(arg));}return DigestUtil.sha1Hex(sb.toString());}private String toString(Object arg) {if (Objects.isNull(arg)) {return "null";}if (arg instanceof Number) {return arg.toString();}return JSONObject.toJSONString(arg);}}

3.接下来定义redisService类

@Component
public class RedisService {public RedisLock tryLock(String lockKey, int expireTime) {String lockValue = UUID.randomUUID().toString();Boolean hasLock = (Boolean)this.redisTemplate.execute((connection) -> {Object nativeConnection = connection.getNativeConnection();String status = null;if (nativeConnection instanceof Jedis) {Jedis jedis = (Jedis)nativeConnection;status = jedis.set(lockKey, lockValue, "nx", "ex", expireTime);} else {JedisCluster jedisx = (JedisCluster)nativeConnection;status = jedisx.set(lockKey, lockValue, "nx", "ex", (long)expireTime);}return "OK".equals(status);});if (hasLock) {return new RedisService.RedisLockInner(this.redisTemplate, lockKey, lockValue);} else {throw new RuntimeException("获取锁失败,lockKey:" + lockKey);}}private class RedisLockInner implements RedisLock {private RedisTemplate redisTemplate;private String key;private String expectedValue;protected RedisLockInner(RedisTemplate redisTemplate, String key, String expectedValue) {this.redisTemplate = redisTemplate;this.key = key;this.expectedValue = expectedValue;}public Object unlock() {final List<String> keys = new ArrayList();keys.add(this.key);final List<String> values = new ArrayList();values.add(this.expectedValue);Object result = this.redisTemplate.execute(new RedisCallback<Long>() {public Long doInRedis(RedisConnection connection) throws DataAccessException {Object nativeConnection = connection.getNativeConnection();return nativeConnection instanceof JedisCluster ? (Long)((JedisCluster)nativeConnection).eval("if redis.call('get',KEYS[1])==ARGV[1]\n then\n   return redis.call('del',KEYS[1])\n else\n   return 0\n end", keys, values) : (Long)((Jedis)nativeConnection).eval("if redis.call('get',KEYS[1])==ARGV[1]\n then\n   return redis.call('del',KEYS[1])\n else\n   return 0\n end", keys, values);}});return result;}public void close() throws Exception {this.unlock();}}
}

4.最后在Controller接口加上注解就行了。

相关文章:

springboot aop实现接口防重复操作

一、前言 有时在项目开发中某些接口逻辑比较复杂&#xff0c;响应时间长&#xff0c;那么可能导致重复提交问题。 二、如何解决 1.先定义一个防重复提交的注解。 import java.lang.annotation.*;Inherited Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Do…...

ubuntu18.04复现yolo v8环境配置之CUDA与pytorch版本问题以及多CUDA版本安装及切换

最近在复现yolo v8的程序&#xff0c;特记录一下过程 环境&#xff1a;ubuntu18.04ros melodic 小知识&#xff1a;GPU并行计算能力高于CPU—B站UP主说的 Ubuntu可以安装多个版本的CUDA。如果某个程序的Pyorch需要不同版本的CUDA&#xff0c;不必删除之前的CUDA&#xff0c;…...

Yaml配置文件读取方法

在日常的代码中&#xff0c;有一些值是配置文件中定义的&#xff0c;这些值可以根据用户的要求进行调整和改变。这往往会写在yaml格式的文件中。这样开放程序给用户时&#xff0c;就可以不必开放对应的源码&#xff0c;只开放yaml格式的配置文件即可。 将配置文件中的值读入程…...

Python3 lambda 函数入门示例 Python lambda 函数

Python lambda 函数 首先&#xff0c;这个语法跟C的语法几乎一样&#xff1b; 通常称 lambda 函数为匿名函数&#xff0c;也称为 丢弃函数&#xff0c;因为应一下子就不要了&#xff0c;不会长期凝结下来形成SDK API&#xff1b;本人觉得它有点类似 inline 函数&#xff0c;或者…...

【计算机网络】HTTPs 传输流程

HTTPS和HTTP的区别 1、HTTP协议传输的数据都是未加密的&#xff0c;是明文的&#xff0c;使用HTTP协议传输隐私信息非常不安 HTTPS协议是由SSLHTTP协议构建的可进行加密传输、身份认证的网络协议&#xff0c;要比http协议安全。 2、HTTPS协议需要到CA申请证书&#xff0c;一般…...

【Linux】国产深度系统装机必备(开发、日常使用)

开发相关工具 IDE推荐官网下载JetBrains Toolbox&#xff0c;后续所有与jetbrains直接全部到toolbox中下载&#xff0c;这里默认所有的app全部放在个人用户下&#xff08;/data/home/计算机用户名/.local/share/JetBrains/Toolbox/apps&#xff09;终端可视化工具&#xff1a;…...

动态规划入门:斐波那契数列模型以及多状态(C++)

斐波那契数列模型以及多状态 动态规划简述斐波那契数列模型1.第 N 个泰波那契数&#xff08;简单&#xff09;2.三步问题&#xff08;简单&#xff09;3.使⽤最⼩花费爬楼梯&#xff08;简单&#xff09;4.解码方法&#xff08;中等&#xff09; 简单多状态1.打家劫舍&#xff…...

LeetCode438.找到字符串中所有字母异位词

因为之前写过一道找字母异位词分组的题&#xff0c;所以这道题做起来还是比较得心应手。我像做之前那道字母异位词分组一样&#xff0c;先把模板p排序&#xff0c;然后拿滑动窗口去s中从头到尾滑动&#xff0c;窗口中的这段字串也给他排序&#xff0c;然后拿这两个排完序的stri…...

【微服务】03-HttpClientFactory与gRpc

文章目录 1.HttpClientFactory &#xff1a;管理外向请求的最佳实践1.1 核心能力1.2 核心对象1.3 HttpClient创建模式 2.gRPC&#xff1a;内部服务间通讯利器2.1 什么是gRPC2.2 特点gRPC特点2.3.NET生态对gRPC的支持情况2.4 服务端核心包2.5 客户端核心包2.5 .proto文件2.6 gRP…...

iOS开发之查看静态库(.a/.framework)中包含的.o文件和函数符号(ar,nm命令)

.a/.framework其实是把编译生成的.o文件&#xff0c;打包成一个.a/.framework文件。a的意思是archive/归档的意思。 查看静态库.a文件包含的内容用下面的命令解压&#xff1a; ar x xxx.a 用ar命令打包静态库&#xff1a; 参数r是将后面的*.o或者*.a文件添加到目标文件中 参数…...

Idea常用快捷键--让你代码效率提升一倍(一)

一、代码编辑相关快捷键 1.单行复制(实现快速创建多个对象)CtrlD 2.空出下一行 ShiftEnter 3.单行注释快捷键 ctrl / 4.快速构建构造函数&#xff0c;setter&#xff0c;getter、toString方法 AltInsert 4.显示快速修复和操作的菜单 altenter 5.格式化代码&#xff1a;C…...

【Open3D】第二篇:GUI编程

文章目录 基本控件创建创建文本框创建button创建布局 绘制形状绘制线段绘制点云 设置属性设置线宽设置点大小 可用Shader汇总GUI框架 基本控件创建 创建文本框 push_edit gui.TextEdit()创建button push_button gui.Button(...) push_button.horizontal_padding_em 0.5 p…...

【Python】P0 本系列博文简介与大纲

Python 前言本系列博文适合谁本系列博文不适合谁本系列博文大纲 前言 本系列博文基于《Python Cookbook》一书&#xff0c;Python 3 版本&#xff1b;本系列博文的目标不是为了构建一个 Python 知识大全&#xff0c;而是为了那些需要快速将 Python 学以致用的相关人员&#xf…...

FL Studio 21.1.0 Build 3713中文破解免费下载安装激活

FL Studio 21是一个功能齐全、开放式的PC音乐创作和制作环境。它具有基于音乐序列器的图形用户界面。 这个数字音频工作站将您所需的一切整合在一个包中&#xff0c;用于创作、编排、录制、编辑、混音和掌握专业质量的音乐。 FL Studio 21是从你的大脑到扬声器的最快方式。制作…...

从0开始配置eslint

没有在.eslintrc文件中配置parserOptions指定语言版本和模块类型 {"parserOptions": {"ecmaVersion": 7, //指定es版本为es2016"sourceType": "module", //使用import导入模块} }eslint还不能识别jsx语法 {"parserOptions"…...

Activity 的启动流程(Android 13)

Activity 的启动过程分为两种&#xff1a;一种是普通 Activity 的启动过程&#xff0c;另一种是根 Activity 的启动过程。普通 Activity 指的是除应用程序启动的第一个 Activity 之外的其他 Activity。根 Activity 指的是应用程序启动的第一个 Activity&#xff0c;因此&#x…...

deepspeed学习资料

记录一些deepspeed学习过程中的好文章 【进行中】1、DeepSpeed使用指南(简略版)_Reza.的博客-CSDN博客 - 含deepspeed的安装方法 - 含 zero config的不同配置&#xff0c;stage1、stage2、stage3的配置和解释...

数据分享|R语言PCA主成分、lasso、岭回归降维分析近年来各国土地面积变化影响...

全文链接&#xff1a;http://tecdat.cn/?p31445 机器学习在环境监测领域的应用&#xff0c;着眼于探索全球范围内的环境演化规律&#xff0c;人类与自然生态之间的关系以及环境变化对人类生存的影响&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 课题着眼于…...

Docker-Consul

Docker-Consul 一、介绍1.什么是服务注册与发现2.什么是consul3.consul提供的一些关键特性&#xff1a; 二、consul 部署1.环境准备2.consul服务器3.查看集群信息4.通过 http api 获取集群信息 三、registrator服务器1.安装 Gliderlabs/Registrator2.测试服务发现功能是否正常3…...

Pygame编程(2)display模块

pygame编程2-display设备显示 pygame.display.init() 初始化 display 模块init() -> None pygame.display.get_init() 获取display初始化 状态&#xff0c;如果已经初始化&#xff0c;返回 True&#xff0c;否则返回Falseget_init() -> bool pygame.display.quit() 退出…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...