自定义注解之防刷新(重复提交)(一)

SpringBoot系列19-SpringBoot防止重复请求,重复表单提交超级简单注解的实现之四(终极版II) SpringBoot防止重复请求,重复表单提交超级简单的注解实现之四(终极版II)原文链接:[https://www.lskyf.com/post/213](https://www.lskyf.com/post/213)前言:防重复提交业务流程图如下1.简化DuplicateSubmitToken.java代码,只留下标记接口,新增超时设置接口2.改造DuplicateSubmitAspect.java新增超时判断代码3.TestController.java测试:包含restful请求,get请求,pos 阅读详情

自定义注解之防刷新(重复提交)

0、前言

​ 在保险行业,有很多场景需要防止用户短时间内重复提交,例如投保单保存操作、提交核保操作、批单保存操作、预提交操作等等,因此我们后端通过注解实现这一功能。

​ 我相信在其他行业应该也会有类似场景,如果有跟好的解决方案欢迎大家一起交流。

1、实现自定义注解–Redis版

1.1、创建自定义注解


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author :
 * @date :Created in 15:11 2022/9/1
 * @description :自定义重复提交注解
 * @version: 1.0
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DuplicateSubmit {
    
    /**
     * 指定时间内不可重复提交,单位:s
     */
    long timeout() default 3;
    
}

1.2、通过AOP切面实现

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHeaders;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
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.text.MessageFormat;
import java.util.concurrent.TimeUnit;

/**
 * @author :
 * @date :Created in 15:11 2022/9/2
 * @description :
 * @version: 1.0
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class DuplicateSubmitAspect {

    private final StringRedisTemplate stringRedisTemplate;

    @Before("@annotation(com.*.aspect.DuplicateSubmit)")
    public void repeatSumbitIntercept(JoinPoint joinPoint) {
        // ip + name  + timeout
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = NetWorkUtils.getRemoteHost(request);
        String jtiInfo = request.getHeader(HttpHeaders.AUTHORIZATION);
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取配置的信息: 名称 过期时间
        DuplicateSubmit annotation = method.getAnnotation(DuplicateSubmit.class);
        String name = annotation.name();
        long timeout = annotation.timeout();
        String key = ip + ":" + jtiInfo;
        log.info(" --- >> 防重提交:key -- {}", key);
        // 判断是否已经超过重复提交的限制时间
        Boolean isAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, key, timeout, TimeUnit.SECONDS);
        if (!isAbsent) {
            String message = MessageFormat.format("请勿在{0}s内重复提交", timeout);
            log.warn("方法:{},{},重复提交", name, ExceptionCode.DUPLICATE_SUBMIT);
            throw new CommonException(ExceptionCode.DUPLICATE_SUBMIT.getCodeValue(), message);
        }

    }
}

1.3、使用

/**
 * @author :
 * @date :Created in 14:13 2022/9/2
 * @description :规则
 * @version: 1.0
 */
@Slf4j
@RestController
@RequiredArgsConstructor
public class RuleConfigController implements IRuleConfigController {

    private final IRuleConfigService iRuleConfigService;

    @Override
    @DuplicateSubmit(name = "ruleConfig:add", timeout = 30)
    public HttpEntity addRuleConfig(@RequestBody RuleConfigRequest ruleConfigRequest) {
        log.info("[ruleConfig add]param-ruleConfigRequest: {}", ruleConfigRequest);
        try {
            RuleConfig ruleConfigNow = iRuleConfigService.addRuleConfig(new RuleConfig(ruleConfigRequest));
            return ResponseObject.createOk(ruleConfigNow);
        }catch (RuntimeException e) {
            log.error("[ruleConfig add]" + e);
            return ResponseObject.createOk(e.getMessage());
        }

    }
}

1.4、补充网络工具类–NetWorkUtils

NetWorkUtils.getRemoteHost(request)获取IP


import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;

/**
 * @author :
 * @date :Created in 16:38 2022/9/13
 * @description :网络工具类
 * @version: 1.0
 */
public class NetWorkUtils {

    /**
     * 获取到 SessionID
     *
     * @param request
     *            HttpServletRequest
     * @return SessionID
     */
    public static String getSessionID(HttpServletRequest request) {
        return request.getSession().getId();
    }

    /**
     * 获取客户端的IP
     *
     * @param request
     *            HttpServletRequest
     * @return 客户端IP
     */
    public static String getRemoteHost(HttpServletRequest request) {

        if (request == null) {
            return null;
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 多个路由时,取第一个非unknown的ip
        final String[] arr = ip.split(",");
        for (final String str : arr) {
            if (!"unknown".equalsIgnoreCase(str)) {
                ip = str;
                break;
            }
        }
        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }

    /**
     * 获取得到本机IP地址
     *
     * @throws SocketException
     */
    public static String getLocalIP() {
        if (isWindowsOS()) {
            return getWindowsIP();
        } else {
            return getLinuxLocalIp();
        }
    }

    /**
     * 判断操作系统是否为windows系统
     */
    public static boolean isWindowsOS() {
        boolean isWindowsOS = false;
        String osName = System.getProperty("os.name");
        if (osName.toLowerCase().indexOf("windows") > -1) {
            isWindowsOS = true;
        }
        return isWindowsOS;
    }

    /**
     * 获取到Host名字
     */
    public static String getLocalHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取WindowsIP
     */
    public static String getWindowsIP() {
        String serverIP = "";
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        byte[] ipAddr = addr.getAddress();
        for (int i = 0; i < ipAddr.length; i++) {
            if (i > 0) {
                serverIP += ".";
            }
            serverIP += ipAddr[i] & 0xFF;
        }
        return serverIP;
    }

    /**
     * 获取LinuxIp
     */
    private static String getLinuxLocalIp() {
        String ip = "";
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                String name = intf.getName();
                if (!name.contains("docker") && !name.contains("lo")) {
                    for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                        InetAddress inetAddress = enumIpAddr.nextElement();
                        if (!inetAddress.isLoopbackAddress()) {
                            String ipaddress = inetAddress.getHostAddress().toString();
                            if (!ipaddress.contains("::") && !ipaddress.contains("0:0:")
                                && !ipaddress.contains("fe80")) {
                                ip = ipaddress;
                            }
                        }
                    }
                }
            }
        } catch (Exception ex) {
            ip = "127.0.0.1";
            ex.printStackTrace();
        }
        return ip;
    }
}

Spring Boot项目里,Apollo配置变了怎么自动刷新@ConfigurationProperties?一个注解搞定 本文详细解析了在Spring Boot项目中如何利用Apollo配置中心实现@ConfigurationProperties的动态刷新。通过@ApolloConfigChangeListener注解和@RefreshScope的巧妙结合,开发者可以轻松实现配置变更的自动感知和Bean刷新,大幅提升微服务架构下的配置管理效率。 阅读详情

相关推荐

SpringBoot自定义注解防止表单重复提交

前言: 本文介绍基于redis和toke机制,自定义@RepeatSubmit注解来实现防止接口重复调用情况。 实现教程: 自定义注解是基于spring aop实现的,所以需要先查看是否引入了aop的依赖,如果没有的话在pom文件中添加以下依赖,还有redis的依赖。 <!-- 引入aop相关 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId&

WuTong_Li 3721

SpringBoot防止重复请求,重复表单提交超级简单的注解实现之三(升级版2)

升级拦截器/** * @description 防止表单重复提交拦截器 */ @Aspect @Component @Slf4j public class DuplicateSubmitAspect { public static final String DUPLICATE_TOKEN_KEY="duplicate_token_key"; @Pointcut("execu...

猿份哥 4384

SpringBoot自定义注解 + AOP 防止重复提交(建议收藏)

Annotation(注解)从JDK 1.5开始, Java增加了对元数据(MetaData)的支持,也就是 Annotation(注解)。注解其实就是代码里的特殊标记,它用于替代配置文件,常见的很多,有 @Override、@Deprecated等什么是元注解元注解是注解的注解,比如当我们需要自定义注解时会需要一些元注解(meta-annotation),如@Target和@Retentionjava内置4种元注解@Target 表示该注解用于什么地方。

王不困的博客 1028

SpringBoot防止重复请求,重复表单提交超级简单的注解实现

1.注解接口 /** * @description 防止表单重复提交注解 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface DuplicateSubmitToken { //保存重复提交标记 默认为需要保存 boolean sav...

weixin_30784141的博客 514

Linux USB Gadget框架解析:从设备模拟到驱动开发实战

USB(通用串行总线)是现代计算机系统中应用最广泛的外设连接标准,其主从动态切换的特性使其在嵌入式系统中扮演着双重角色。与I2C、SPI等固定主从架构不同,USB设备需要同时支持主机(Host)和从机(Device)两种模式,这催生了Linux内核中独特的USB Gadget框架。该框架通过分层设计,将硬件控制器驱动(UDC)、功能逻辑(Function)和配置管理(Configfs)解耦,实现了灵活的设备模拟能力。在工程实践中,开发者可以利用Gadget框架快速实现U盘、网络适配器、串口转换器等常见USB

weixin_28007513的博客 563

脑影像分析工具JuSpace:跨模态关联的神经递质系统研究利器

JuSpace是一款专业的脑影像分析软件,专为跨模态关联研究设计。它能够将功能或结构磁共振影像与神经递质PET/SPECT图谱进行空间关联分析,帮助研究者探索宏观脑影像异常与微观神经化学系统(如多巴胺、血清素)之间的潜在联系,是神经精神疾病机制研究的强大工具。

c2d3e4f的博客 544

SpringBoot防止重复请求,重复表单提交超级简单的注解实现之四(终极版I)

2018.11.30更新 前言:上篇文章有的童鞋说不行啊,怎么不能防止重复提交呢! 首先需要说明的是之前的防止重复提交是指:一次请求完成之前防止重复提交,当然扩展下就可以做到会话间防止重复提交,还可以扩展为某个时间段或者永久防止重复提交(这个我就不实现了),下面我来扩展一下相同会话防止重复提交其实很简单 在上一篇的基础上DuplicateAspect不移除标记为SESSION的token就可...

猿份哥 1万+

android6 usb默认文件管理,不弹RSA配对提示框

本文修改针对 高通 Android6.0.1 出user版本时 https://www.cnblogs.com/Jokeyyu/p/7424262.html http://blog.youkuaiyun.com/sinc00/article/details/44957943 diff --git a/frameworks/base/packages/SystemUI/src/com/android...

木叶风的博客 692

java后端表单重复提交_【分享】表单重复提交后端限制

在首页看到基于前段并行控制的表单重复提交限制,觉得不用那么复杂,再此献上后端限制代码1)注解类packageplus.jfinal.annotations;importjava.lang.annotation.*;/***重复提交Token*/@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpu...

weixin_27183235的博客 357

AOSP14 Launcher3——手势上滑关键类AbsSwipeHandler解析

通过精密的事件监听、状态管理和对系统动画 API 及 Launcher 内部组件的协调,实现了复杂而流畅的手势导航体验。它的抽象设计允许子类(如。是 Quickstep (Launcher3 的手势导航实现) 中处理。的核心逻辑抽象基类。这个非常核心且复杂的类。)定制特定的回家动画逻辑。好的,我们来深入分析。

Micheal_Yang's Blog 1521

领域驱动新实践:COLA框架全解析——架构设计与实战案例解析

定义订单支付事件java代码解读复制代码} }Order定义Order类,继承并实现支付逻辑:java代码解读复制代码} }定义接口:java代码解读复制代码定义类,处理订单支付逻辑并持久化:java代码解读复制代码} }定义和处理订单支付事件:java代码解读复制代码} }java代码解读复制代码} }定义和接口及其实现:java代码解读复制代码java代码解读复制代码。

2401_89221445的博客 2668

spring boot 学习(七)小工具篇:表单重复提交

学习 Spring Boot 中,我想将我在项目中添加几个我在 SpringMVC 框架中常用的工具类(主要都是涉及到 Spring AOP 部分知识)。接下来的时间,我尝试将这些框架整合到 Spring Boot 中(**尽可能完成**),毕竟项目开发中这些工具是非常有用的。

FunriLy 7856

【WILEY】从Revision到Online:逐层拆解投稿状态更新与关键文件提交实战

本文详细解析了学术论文从返修到在线发表的全过程,重点拆解了投稿系统状态更新背后的含义与应对策略。文章提供了返修稿与回复信的准备技巧、关键补充文件(如图文摘要)的提交要点,并分享了校对校样与处理出版协议的实战经验,帮助作者高效、专业地完成发表流程。

weixin_29209805的博客 715

前后端防重复提交的 6 种落地实现:从按钮禁用到 AOP 全自动防护

目标:通过注解自动实现防重,业务代码零污染。步骤定义注解:// 超时时间(秒)AOP 切面拦截:@Aspect@Component@Autowired// 生成唯一 key:类名 + 方法名 + 参数摘要(可用 MD5)throw new RuntimeException("操作过于频繁,请勿重复提交");// 示例:使用方法签名 + 参数 JSON 的 MD5在 Controller 方法上加注解:// 业务逻辑,无需写防重代码优点全自动、可配置、复用性强。注意。

2603_94941239的博客 684

AI算力网络与通信领域QoS保障的测试方法

在当今数字化时代,AI技术蓬勃发展,AI算力网络应运而生。它将分布在不同地理位置的算力资源进行整合,以高效地支持各种AI应用,如深度学习训练、智能图像识别等。而通信领域则是信息传输的基石,确保数据能够准确、快速地在不同节点之间传递。QoS保障在通信领域至关重要,它可以确保不同类型的业务(如语音通话、视频会议、在线游戏等)获得与其需求相匹配的服务质量。在AI算力网络中,QoS保障同样不可或缺,因为AI任务对网络的带宽、延迟、可靠性等指标有严格要求。

AI算力网络与通信的博客 529

基于YOLOv12的焊接质量检测系统:从原理到实现的完整指南

python"""标准卷积块: Conv + BatchNorm + SiLU""""""跨阶段部分连接块"""])"""YOLOv12颈部网络 - 特征金字塔"""p3, p4, p5 = features # 不同尺度的特征图# 上采样路径x = self.csp1(x) # 中尺度特征y = self.csp2(y) # 小尺度特征# 下采样路径z = self.csp3(z) # 中尺度特征。

YOLO 186

SpringBoot+Vue在线考试系统:从演示项目到工程级解决方案的升级指南

在软件开发领域,构建一个高可用、高性能的Web应用是核心目标。这涉及到对系统架构、数据一致性和业务逻辑的深入理解。前后端分离架构通过清晰的API契约实现职责分离,而工程化实践则关注于解决真实场景中的稳定性挑战。例如,在高并发场景下,如何保障数据一致性、优化响应时间成为关键。通过引入缓存机制、异步处理和策略模式,可以显著提升系统的扩展性和可维护性。在线考试系统作为一个典型应用,其核心价值在于处理考试过程中的实时性、防作弊和答案持久化等复杂需求。本文聚焦于如何将基础的SpringBoot和Vue技术栈项目,升级

weixin_30376509的博客 402

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

自定义防止重复提交注解@PreventDuplicateSubmit,Spring Aop

【代码】自定义防止重复提交注解@PreventDuplicateSubmit,Spring Aop。

Face_To_Object的博客 410

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

spring自定义注解,防止页面重复提交

注解: @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DuplicateSubmitToken { boolean bindToken() default true; boolean unbindToken() default true; } 拦截

yangliuhbhd的博客 1877

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

防重复提交(注解+AOP)

第一步:定义防刷注解、启动类开启切面支持、pom引入依赖 @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestRepeatAnnotation { //允许访问的次数,默认值MAX_VALUE int count() default Integer.MAX_VALUE; // 时间段,单.

Be_insighted的博客 2162

自定义注解-SpringBoot防重复提交AOP方法

*** 自定义注解防止表单重复提交*/@Inherited/*** 间隔时间(ms),小于此时间视为重复提交*//*** 提示消息*/public String message() default "不允许重复提交,请稍候再试";

qq_63431773的博客 1339

防重复提交注解

【代码】防重复提交注解。

xiaoshitou_2015的博客 318

防止重复提交 AOP+注解方式实现

幂等性问题 Http/1.1中幂等性的定义:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就说,任意多次执行对资源本身所产生的影响均与一次执行的影响相同 什么情况下需要幂等(场景) 业务中经常会遇到重复提交的情况,无论是网络问题无法收到请求结果而重新发起请求,还是前端操作造成重复提交情况。 交易、支付系统中:这种重复提交尤为明显:用户连续点击多次提交订单,后台应该只产生一个订单 声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据的状态发生多次

qq_41884536的博客 1170

若依框架——防重复提交自定义注解

java代码解读复制代码/** * 自定义注解防止表单重复提交 * * @author ruoyi * */ @Inherited @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeatSubmit { /** * 间隔时间(ms),小于此时间视为重复提交 */ public int interval() default 5000;

2401_89221867的博客 1365

java 防重复提交_Java中如何避免重复提交请求

点击上方蓝字关注我们一、背景我们在使用系统过程中,经常碰到这种情况:网页响应很慢,提交请求后发现没反应,然后就会反复点击提交按钮。查看后台日志后发现一个同样的请求提交了多次,后果就是轻则导致产生多条重复数据,重则直接导致程序异常。那么,有没有什么办法可以避免这种问题呢?二、产生原因导致重复请求的原因很多,大体为以下几种:多次点击提交按钮反复刷新页面点击浏览器后退按钮,导致重复提交表单浏览器重复的H...

weixin_33737261的博客 9805
上一篇: 自定义注解之单机版限流
下一篇: 1、MySQL及sum case when统计实战讲解
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白de成长之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值