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、请求时间以及随机数等生成一个更复杂的唯一标识符。

总结

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

相关文章

  • Spring Boot 集成 ElasticSearch应用小结

    Spring Boot 集成 ElasticSearch应用小结

    这篇文章主要介绍了Spring Boot 集成 ElasticSearch应用小结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-11-11
  • Java中死锁的原理实战分析

    Java中死锁的原理实战分析

    这篇文章主要介绍了Java中死锁的原理,结合具体案例形式分析了java死锁形成的相关原理,需要的朋友可以参考下
    2019-08-08
  • IDEA新建springboot项目时未生成pom.xml文件的解决操作

    IDEA新建springboot项目时未生成pom.xml文件的解决操作

    这篇文章主要给大家介绍了关于IDEA新建springboot项目时未生成pom.xml文件的解决操作方法,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-02-02
  • Java Web stmp发送带附件邮件(附SSL版)

    Java Web stmp发送带附件邮件(附SSL版)

    这篇文章主要为大家详细介绍了Java Web stmp发送带附件邮件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • java Signleton模式详解及示例代码

    java Signleton模式详解及示例代码

    Singleton模式是创建模式。这种模式只涉及一个类是负责创建自己的对象。该类确保只有一个对象获得创建。这个类提供了一种方法来访问它的唯一对象
    2016-10-10
  • MyBatis-Plus updateById方法更新不了空字符串/null的问题及解决

    MyBatis-Plus updateById方法更新不了空字符串/null的问题及解决

    MyBatis-Plus的updateById()方法在更新字段为null时会失败,因为默认策略不更新null值,文章提供三种解决方案:全局配置忽略判断、使用PO对象的el属性指定jdbcType、通过注解设置字段验证策略为IGNORED
    2026-03-03
  • java实现画图板上画一条直线

    java实现画图板上画一条直线

    这篇文章主要为大家详细介绍了java实现画图板上画一条直线,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • java一个接口多个实现类的调用方式

    java一个接口多个实现类的调用方式

    这篇文章主要给大家介绍了关于java一个接口多个实现类的调用方式的相关资料,经测试确认,当一个接口有多个实现时,调用时只会执行一个,有时候需要多个实现调用,需要的朋友可以参考下
    2023-09-09
  • Java如何获取指定目录文件列表

    Java如何获取指定目录文件列表

    这篇文章主要介绍了Java如何获取指定目录文件列表,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • RabbitMQ幂等性与优先级及惰性详细全面讲解

    RabbitMQ幂等性与优先级及惰性详细全面讲解

    关于MQ消费者的幂等性问题,在于MQ的重试机制,因为网络原因或客户端延迟消费导致重复消费。使用MQ重试机制需要注意的事项以及如何解决消费者幂等性与优先级及惰性问题以下将逐一讲解
    2022-11-11

最新评论