springboot防止表单重复提交方式

 更新时间:2025年12月11日 14:41:52   作者:svygh123  
本文介绍了两种防止SpringBoot应用中表单重复提交的方法:第一种是使用Redis来实现,通过设置键值对和检查唯一标识来防止重复提交;第二种是使用SpringAOP,通过创建切面和自定义注解来统一处理防重复提交,减少重复代码并保持业务逻辑清晰

第一种方法:单个防止

在Spring Boot应用中使用Redis来防止表单的重复提交,可以通过以下几个步骤来实现:

步骤 1:添加依赖

确保你的项目中添加了Spring Boot Starter Data Redis和Spring Boot Starter Web依赖。

pom.xml文件中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

步骤 2:配置Redis

application.propertiesapplication.yml中配置Redis服务器的信息:

spring.redis.host=localhost
spring.redis.port=6379

步骤 3:创建一个工具类处理Redis操作

创建一个工具类来封装Redis的一些常用操作,比如设置键值对、获取键值对等。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class RedisUtil {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public void delete(String key) {
        stringRedisTemplate.delete(key);
    }
}

步骤 4:实现防重逻辑

在Controller中使用上面的工具类来实现防重逻辑。

通常的做法是在请求开始时检查Redis中是否存在该请求的唯一标识(如用户ID + 时间戳),如果不存在则允许请求继续,并将此标识存入Redis;如果存在,则直接返回错误信息。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @Autowired
    private RedisUtil redisUtil;

    @PostMapping("/submit")
    public String submit(@RequestParam("userId") String userId) {
        // 使用用户ID和时间戳作为key,防止并发下的重复提交
        String key = "form_submit_" + userId + "_" + System.currentTimeMillis();

        if (redisUtil.get(key) != null) {
            return "请求重复,请勿重复提交!";
        } else {
            // 执行业务逻辑
            // ...

            // 将key存入Redis并设置过期时间
            redisUtil.set(key, "1", 5, TimeUnit.MINUTES);
            return "提交成功!";
        }
    }
}

请注意:

在实际应用中,你可能需要根据具体需求调整上述代码,比如更改存储在Redis中的键的生成方式、设置更合理的过期时间等。

此外,为了提高性能和避免并发问题,可以考虑使用Redis的原子操作,例如SETNX命令。

第二种方法:aop全局方法

使用AOP(面向切面编程)来统一处理防重复提交是一种很好的实践,它能减少重复代码并保持业务逻辑的清晰性。下面是如何使用Spring AOP来实现这一功能的示例。

首先,你需要在项目中添加Spring AOP的支持,通常情况下Spring Boot项目已经默认包含AOP支持。

步骤 1:创建切面类

创建一个切面类,用于定义防重提交的逻辑。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class AntiDuplicateSubmissionAspect {

    private static final String KEY_PREFIX = "anti_duplicate_";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(antiDuplicateSubmission)")
    public Object antiDuplicateSubmit(ProceedingJoinPoint joinPoint, AntiDuplicateSubmission antiDuplicateSubmission) throws Throwable {
        // 从注解中获取参数名
        String paramName = antiDuplicateSubmission.value();
        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        // 假设参数位置已知,或者通过参数名获取参数值
        String userId = (String) args[0];

        // 生成唯一标识符
        String key = KEY_PREFIX + userId + "_" + System.currentTimeMillis();

        // 检查是否已经存在
        if (stringRedisTemplate.hasKey(key)) {
            throw new RuntimeException("请求重复,请勿重复提交!");
        }

        // 设置过期时间,例如5分钟
        stringRedisTemplate.opsForValue().set(key, "1", 5, TimeUnit.MINUTES);

        try {
            return joinPoint.proceed();
        } finally {
            // 清理key,这一步在大多数情况下可选,因为设置了过期时间
            stringRedisTemplate.delete(key);
        }
    }
}

步骤 2:创建自定义注解

创建一个自定义注解,用于标记需要防重提交的控制器方法。

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AntiDuplicateSubmission {
    String value();
}

步骤 3:应用注解

在需要防重提交的Controller方法上应用这个注解。

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @PostMapping("/submit")
    @AntiDuplicateSubmission(value = "userId")
    public String submit(@RequestParam("userId") String userId) {
        // 执行业务逻辑
        // ...
        return "提交成功!";
    }
}

这样,每次有请求到达带有@AntiDuplicateSubmission注解的方法时,AOP切面会先执行防重检查,如果发现重复提交,则阻止业务逻辑执行并抛出异常。

注意:

这里的AntiDuplicateSubmission注解的value属性应该与请求参数名匹配,以便正确地获取到用户ID或其他用于生成唯一标识符的参数。

同时,使用System.currentTimeMillis()生成的时间戳作为一部分唯一标识符可能在高并发场景下存在并发问题,你可能需要一个更安全的唯一生成策略,例如UUID或者结合用户ID、请求时间以及随机数等生成一个更复杂的唯一标识符。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 拳皇(Java简单的小程序)代码实例

    拳皇(Java简单的小程序)代码实例

    这篇文章主要介绍了拳皇Java简单小程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Springboot主程序类注解配置过程图解

    Springboot主程序类注解配置过程图解

    这篇文章主要介绍了Springboot主程序类注解配置过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • AbstractQueuedSynchronizer内部类Node使用讲解

    AbstractQueuedSynchronizer内部类Node使用讲解

    这篇文章主要为大家介绍了AbstractQueuedSynchronizer内部类Node使用讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • Java编程用栈来求解汉诺塔问题的代码实例(非递归)

    Java编程用栈来求解汉诺塔问题的代码实例(非递归)

    这篇文章主要介绍了Java编程用栈来求解汉诺塔问题的代码实例(非递归),具有一定参考价值,这里给大家分享下,供朋友们参考。
    2017-10-10
  • SpringBoot配置文件之properties和yml的使用

    SpringBoot配置文件之properties和yml的使用

    这篇文章主要介绍了SpringBoot配置文件之properties和yml的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • Java的@Transactional、@Aysnc、事务同步问题详解

    Java的@Transactional、@Aysnc、事务同步问题详解

    这篇文章主要介绍了Java的@Transactional、@Aysnc、事务同步问题详解,现在我们需要在一个业务方法中插入一个用户,这个业务方法我们需要加上事务,然后插入用户后,我们要异步的方式打印出数据库中所有存在的用户,需要的朋友可以参考下
    2023-11-11
  • java 实现判断回文数字的实例代码

    java 实现判断回文数字的实例代码

    这篇文章主要介绍了java 实现判断回文数字的实例代码的相关资料,需要的朋友可以参考下
    2017-03-03
  • SpringBoot如何实现同域SSO(单点登录)

    SpringBoot如何实现同域SSO(单点登录)

    单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。即在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,本文将介绍SpringBoot如何实现同域SSO(单点登录)
    2021-05-05
  • java手动实现常见数据结构的示例代码

    java手动实现常见数据结构的示例代码

    本文介绍了Java中常用数据结构的特点和Java实现,包括数组、动态数组、链表、栈、队列、哈希表、树、堆、图、集合、双向队列以及自定义链表,帮助开发者选择合适的数据结构以提升代码效率,感兴趣的朋友一起看看吧
    2025-02-02

最新评论