java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java防止表单重复提交的注解@RepeatSubmit

java如何防止表单重复提交的注解@RepeatSubmit

作者:东方巴黎~Sunsiny

@RepeatSubmit是一个自定义注解,用于防止表单重复提交,它通过AOP和拦截器模式实现,结合了线程安全和分布式环境的考虑,注解参数包括interval(间隔时间)和message(提示信息),使用时需要注意并发处理、用户体验、性能和安全性等方面,失效原因是多方面的

代码解释

@RepeatSubmit

以下是一些常见的参数和用法:

示例代码

假设有一个 @RepeatSubmit 注解的定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
    String value() default "";
    int interval() default 3000; // 默认3秒
    String message() default "请勿重复提交";
}

使用示例

在控制器方法中使用 @RepeatSubmit 注解:

@RestController
public class UserController {

    @PostMapping("/submitForm")
    @RepeatSubmit(interval = 5000, message = "请等待5秒后再提交")
    public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
        // 处理表单提交逻辑
        return ResponseEntity.ok("表单提交成功");
    }
}

控制流图

以下是 @RepeatSubmit 注解的控制流图,展示了其工作原理:

flowchart TD
A[开始] --> B[接收请求]
B --> C{检查是否重复提交}
C -->|是| D[返回重复提交提示信息]
C -->|否| E[处理请求]
E --> F[返回成功响应]
F --> G[结束]

说明

使用的设计模式

@RepeatSubmit 注解通常结合 AOP(面向切面编程) 和 拦截器模式 来实现防止表单重复提交的功能。

设计模式解析

AOP(面向切面编程):

拦截器模式

定义注解

   @Target(ElementType.METHOD)
   @Retention(RetentionPolicy.RUNTIME)
   public @interface RepeatSubmit {
       String value() default "";
       int interval() default 3000; // 默认3秒
       String message() default "请勿重复提交";
   }
   

创建切面

   @Aspect
   @Component
   public class RepeatSubmitAspect {

       @Around("@annotation(repeatSubmit)")
       public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
           // 获取方法签名
           MethodSignature signature = (MethodSignature) joinPoint.getSignature();
           Method method = signature.getMethod();

           // 获取请求上下文
           HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

           // 获取注解参数
           int interval = repeatSubmit.interval();
           String message = repeatSubmit.message();

           // 从 session 中获取上次请求的时间戳
           Long lastRequestTime = (Long) request.getSession().getAttribute(method.getName());

           // 检查是否重复提交
           if (lastRequestTime != null && System.currentTimeMillis() - lastRequestTime < interval) {
               throw new RuntimeException(message);
           }

           // 记录当前请求的时间戳
           request.getSession().setAttribute(method.getName(), System.currentTimeMillis());

           // 继续执行目标方法
           return joinPoint.proceed();
       }
   }
   

在控制器方法中使用注解

   @RestController
   public class UserController {

       @PostMapping("/submitForm")
       @RepeatSubmit(interval = 5000, message = "请等待5秒后再提交")
       public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
           // 处理表单提交逻辑
           return ResponseEntity.ok("表单提交成功");
       }
   }
   

使用@RepeatSubmit需要注意什么

使用 @RepeatSubmit 注解来防止表单重复提交时,需要注意以下几个方面:

1. 注解参数配置

2. 并发处理

3. 用户体验

4. 性能考虑

5. 安全性

6. 日志记录

示例代码

以下是一个更完善的 @RepeatSubmit 注解和切面实现,考虑了上述注意事项:

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
    String value() default "";
    int interval() default 3000; // 默认3秒
    String message() default "请勿重复提交";
}
// 创建切面
@Aspect
@Component
public class RepeatSubmitAspect {

    @Autowired
    private RedisTemplate<String, Long> redisTemplate;

    @Around("@annotation(repeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取请求上下文
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // 获取注解参数
        int interval = repeatSubmit.interval();
        String message = repeatSubmit.message();

        // 生成唯一的请求标识
        String key = method.getName() + ":" + request.getRemoteAddr();

        // 从 Redis 中获取上次请求的时间戳
        Long lastRequestTime = redisTemplate.opsForValue().get(key);

        // 检查是否重复提交
        if (lastRequestTime != null && System.currentTimeMillis() - lastRequestTime < interval) {
            throw new RuntimeException(message);
        }

        // 记录当前请求的时间戳
        redisTemplate.opsForValue().set(key, System.currentTimeMillis(), interval, TimeUnit.MILLISECONDS);

        // 继续执行目标方法
        return joinPoint.proceed();
    }
}
// 在控制器方法中使用注解
@RestController
public class UserController {

    @PostMapping("/submitForm")
    @RepeatSubmit(interval = 5000, message = "请等待5秒后再提交")
    public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
        // 处理表单提交逻辑
        return ResponseEntity.ok("表单提交成功");
    }
}

控制流图

以下是 @RepeatSubmit 注解的控制流图,展示了其工作原理:

flowchart TD
    A[开始] --> B[接收请求]
    B --> C[生成唯一请求标识]
    C --> D[从 Redis 获取上次请求时间戳]
    D -->|存在且未过期| E[返回重复提交提示信息]
    D -->|不存在或已过期| F[记录当前请求时间戳]
    F --> G[继续执行目标方法]
    G --> H[返回成功响应]
    H --> I[结束]

说明

使用@RepeatSubmit会失效吗

使用 @RepeatSubmit 注解来防止表单重复提交时,确实可能会遇到一些情况下失效的问题。

以下是一些常见的失效原因及解决方案:

1. 前端快速连续点击

2. 网络延迟

3. 会话失效

4. 并发请求

5. 时间戳精度问题

6. 代码逻辑错误

总结

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

您可能感兴趣的文章:
阅读全文