您的位置:

防重复提交的综合探究

一、防重复提交 AOP

在项目中,我们经常会遇到用户不小心重复提交的情况,既会造成服务器负载过高,又会影响用户体验,因此我们需要对此进行防范处理。其中一种方法是使用AOP面向切面编程的方式解决。具体步骤如下:

1、首先,我们需要定义一个注解,用于标记需要进行防重复提交处理的方法或类,如下所示:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicateSubmit {
}

2、然后,定义一个切面来拦截被标记的方法或类,并在拦截的过程中对重复提交进行处理,如下所示:

@Component
@Aspect
public class PreventDuplicateSubmitAspect {

    @Autowired
    private Cache cache;

    @Around("@annotation(com.example.demo.annotation.PreventDuplicateSubmit)")
    public Object preventDuplicateSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String sessionId = attributes.getSessionId();
        String methodName = joinPoint.getSignature().toLongString();

        if (cache.getIfPresent(sessionId + methodName) == null) {
            cache.put(sessionId + methodName, 1);
            Object result = joinPoint.proceed();
            return result;
        } else {
            throw new RuntimeException("请勿重复提交");
        }
    }
}

  

3、使用定义好的注解标记需要进行防重复提交处理的方法或类,如下所示:

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/submit")
    @PreventDuplicateSubmit
    public String submit(@RequestBody Map params) {
        // ...
    }

    // ...
}

  

通过以上步骤,我们就可以使用AOP来防止重复提交了。

二、防止请求重复提交

在使用AOP进行防重复提交时,我们并没有考虑到两个不同的请求可能会同时进入同一个方法,从而引发重复提交。因此,我们需要在方法内部进行处理,以避免这种情况的出现。具体步骤如下:

1、在方法内部添加一个锁,如下所示:

@PostMapping("/submit")
public String submit(@RequestBody Map params) {
    synchronized (this) {
        // ...
    }
}

  

2、使用一个Set集合来保存已经提交过的参数,如下所示:

private Set submittedParams = new HashSet<>();

@PostMapping("/submit")
public String submit(@RequestBody Map
    params) {
    if (!submittedParams.add(params.toString())) {
        throw new RuntimeException("请勿重复提交");
    }
    // ...
}

   
  

通过以上两种方式,我们可以有效地防止请求重复提交。

三、防重复提交注解

在使用AOP的方式进行防重复提交时,我们需要手动在每个需要进行防重复提交处理的方法或类上添加一个注解。为了方便使用,我们可以自定义一个注解,并通过注解方式来实现防重复提交。具体步骤如下:

1、定义一个注解,如下所示:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicateSubmit {
}

2、定义一个切面,并在切面上使用该注解,如下所示:

@Component
@Aspect
public class PreventDuplicateSubmitAspect {

    @Autowired
    private Cache cache;

    @Around("@annotation(com.example.demo.annotation.PreventDuplicateSubmit)")
    public Object preventDuplicateSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String sessionId = attributes.getSessionId();
        String methodName = joinPoint.getSignature().toLongString();

        if (cache.getIfPresent(sessionId + methodName) == null) {
            cache.put(sessionId + methodName, 1);
            Object result = joinPoint.proceed();
            return result;
        } else {
            throw new RuntimeException("请勿重复提交");
        }
    }
}

  

3、使用自定义的注解标记需要进行防重复提交处理的方法或类,如下所示:

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/submit")
    @PreventDuplicateSubmit
    public String submit(@RequestBody Map params) {
        // ...
    }

    // ...
}

  

通过以上步骤,我们可以使用自定义注解的方式来防止重复提交。

四、防重复提交 AOP Express

Spring AOP的使用有时候太过繁琐,因此我们可以使用AOP Express来简化操作。具体步骤如下:

1、在pom.xml文件中引入AOP Express相关依赖包,如下所示:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>
<dependency>
    <groupId>com.googlecode.easyspring</groupId>
    <artifactId>easy-aop-express</artifactId>
    <version>2.2.0</version>
</dependency>

2、在方法或类上添加一个AOP Express的注解,如下所示:

@MethodAround("execution(* com.example.demo.controller.TestController.submit(..))")
@PreventDuplicateSubmit
public Object preventDuplicateSubmit(JoinPoint joinPoint) throws Throwable {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    String sessionId = attributes.getSessionId();
    String methodName = joinPoint.getSignature().toLongString();

    if (cache.getIfPresent(sessionId + methodName) == null) {
        cache.put(sessionId + methodName, 1);
        Object result = joinPoint.proceed();
        return result;
    } else {
        throw new RuntimeException("请勿重复提交");
    }
}

通过以上步骤,我们可以使用AOP Express来进行防重复提交处理。

五、防重复提交的最佳方案

不同的场景下,我们需要采用不同的方式进行防重复提交处理。对于高并发、分布式的应用程序,我们需要使用一种能够支持分布式锁的最佳方案来保证数据的一致性和安全性。具体步骤如下:

1、配置Redis分布式锁相关信息,如下所示:

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0

2、定义一个分布式锁的工具类,如下所示:

@Component
public class RedisDistributedLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 获取锁
     * @param key 锁的名字
     * @param value 锁的值
     * @param expireTime 锁的过期时间
     * @return 是否获取锁成功
     */
    public boolean tryLock(String key, String value, int expireTime) {
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        String result = connection.execute("SET", key.getBytes(), value.getBytes(), "NX".getBytes(), "EX".getBytes(), String.valueOf(expireTime).getBytes());
        connection.close();
        return result != null && result.equalsIgnoreCase("OK");
    }

    /**
     * 释放锁
     * @param key 锁的名字
     * @param value 锁的值
     */
    public void unlock(String key, String value) {
        List keys = new ArrayList<>();
        keys.add(key);
        List
    values = new ArrayList<>();
        values.add(value);
        Long result = redisTemplate.execute(new DefaultRedisScript<>(releaseLockScript, Long.class), keys, values);
    }
}

   
  

3、在方法内部使用分布式锁进行防重复提交处理,如下所示:

@PostMapping("/submit")
public String submit(@RequestBody Map params) {
    String key = "submit_lock";
    String value = UUID.randomUUID().toString();
    int expireTime = 60;
    if (!redisDistributedLock.tryLock(key, value, expireTime)) {
        throw new RuntimeException("请勿重复提交");
    }
    try {
        // ...
    } finally {
        redisDistributedLock.unlock(key, value);
    }
}

  

通过以上步骤,我们就可以在高并发、分布式的环境下使用分布式锁来实现防重复提交的最佳方案。

六、防重复提交解决方案

以上方案在不同的场景下都可以有效地解决防重复提交的问题。在实际应用中,我们需要根据具体项目需求来选择适合自己的方案。同时,我们可以将上述各种方案进行组合使用,以达到更加全面、完善的防重复提交解决方案。

七、防重复提交分布式锁最好吗

防重复提交分布式锁是目前最好的一种解决方案,它不仅可以防止重复提交,还可以保证数据的一致性和安全性。但是,在实际应用中,我们还需要考虑到分布式锁的性能、可用性、并发性等方面的问题。因此,在使用分布式锁时,我们需要注意分布式锁算法的选择、锁的粒度、锁的超时时间等问题,以便在最大程度上提升系统的性能和可用性。

八、SpringBoot防重复提交

SpringBoot框架为我们提供了很多便捷的方式来实现防重复提交。在本文中,我们已经使用了很多SpringBoot的功能,如AOP、缓存、Redis等。因此,在使用SpringBoot进行开发时,我们可以直接使用这些功能来实现防重复提交处理,而不需要额外引入其他第三方框架。

九、前端防止重复提交

在前端,我们可以采用禁止重复提交按钮、延时提交、加载提示等方式来防止重复提交,具体实现方式如下:

1、禁止重复提交按钮

在提交按钮触发以后,将按钮设置为不可用状态,在提交请求响应以后将按钮重新设置为可用状态,从而避免用户重复点击提交按钮。

$("#submitBtn").click(function() {
    $(this).attr("disabled", true);
    $.ajax({
        type: "POST",
        url: "/submit",
        data: param,
        success: function() {
            $("#submitBtn").attr("disabled", false);
            // ...
        },
        error: function() {
            $("#submitBtn").attr("disabled", false);
            // ...
        }
    });
});

2、延时提交

在提交按钮触发以后,延迟一定时间再进行提交,这样即使用户多次点击提交按钮,也只会进行一次提交操作。

$("#submitBtn").click(function() {
    $(this).attr("disabled", true);
    setTimeout(function() {
        $.ajax({
            type: "POST",
            url: "/submit",
            data: param,
            success: function() {
                $("#submitBtn").attr("disabled", false);
                // ...
            },
            error: function() {
                $("#submitBtn").attr("disabled", false);
                // ...
            }
        });
    }, 1000);
});

3、加载提示

在提交请求响应以前,显示一个加载提示框,从而让用户知道他们已经提交了数据,并且需要等待服务器的响应。