【源码解析】防重于未然:自定义@RepeatSubmit注解实现接口防重复提交

引言

在Web应用中,由于网络延迟、用户误操作或是前端表单设计不当,很容易出现用户在短时间内多次点击提交按钮的情况。这种重复提交不仅可能造成数据库数据的不一致,还可能导致不必要的服务器负载,甚至产生财务上的损失(如重复支付)。因此,实现有效的防重复提交机制显得尤为重要。本文将引导你如何通过自定义@RepeatSubmit注解,在Spring框架中优雅地解决这一问题。


定制防重注解:@RepeatSubmit

首先,我们定义一个自定义注解@RepeatSubmit,用于标记需要防重复提交的控制器方法。

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 RepeatSubmit {
    long lockTimeInSeconds() default 30; // 锁定时间,单位秒}

实现防重切面:RepeatSubmitAspect

接下来,我们创建一个名为RepeatSubmitAspect的切面,它将利用Redis或类似的数据存储来记录每个请求的状态,从而实现防重复提交的功能。

import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.util.concurrent.TimeUnit;@Aspect@Componentpublic class RepeatSubmitAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Around("@annotation(repeatSubmit)")
    public Object preventRepeatSubmission(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        String key = generateKey(joinPoint);
        Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(key, "locked", repeatSubmit.lockTimeInSeconds(), TimeUnit.SECONDS);

        if (lockAcquired != null && !lockAcquired) {
            throw new RepeatSubmitException("Request is duplicated. Please try again later.");
        }

        try {
            return joinPoint.proceed();
        } finally {
            // 清理锁,这里假设锁超时时间已足够长,一般情况下无需清理
        }
    }

    private String generateKey(ProceedingJoinPoint joinPoint) {
        // 可以基于用户ID、请求参数等生成key
        return joinPoint.getSignature().toString();
    }}

异常处理:RepeatSubmitException

当检测到重复提交时,切面将抛出RepeatSubmitException异常,以便前端或中间件能够捕获并作出相应的响应。

public class RepeatSubmitException extends RuntimeException {
    public RepeatSubmitException(String message) {
        super(message);
    }}

集成与测试

确保你的Spring应用上下文中包含了RepeatSubmitAspect组件。你可以在控制器中使用@RepeatSubmit注解来标记需要防重复提交的方法

@RestController@RequestMapping("/api")public class MyController {

    @PostMapping("/submit")
    @RepeatSubmit(lockTimeInSeconds = 60)
    public String submitForm(@RequestBody FormData formData) {
        // 处理表单数据
        return "Form submitted successfully";
    }}

通过自动化测试或手动发送请求,验证防重复提交机制是否有效工作。


结语

本文介绍了如何在Spring框架中,通过自定义@RepeatSubmit注解和RepeatSubmitAspect切面,实现接口的防重复提交功能。这种机制不仅增强了应用的鲁棒性,也提升了用户体验,避免了因重复提交带来的潜在问题。

如果你对本文的内容感兴趣,或想深入了解更多的编程技巧、源码分析及最佳实践,欢迎加入我的知识星球社区。在那里,你将与一群志同道合的技术爱好者一起,探索更广阔的技术世界。


最后,我希望这篇文章能够帮助你构建更加安全、可靠的Web应用程序。如果你有任何疑问或想要进一步交流,请随时留言或私信,期待与你共同成长!


注意:本文中的示例代码和策略适用于基本的防重复提交场景,但在生产环境中,你可能需要考虑更复杂的因素,比如分布式环境下的锁机制、并发控制和事务管理等,以确保系统的稳定性和安全性。


来源: 互联网
本文观点不代表源码解析立场,不承担法律责任,文章及观点也不构成任何投资意见。

赞 ()

相关推荐

发表回复

评论列表

点击查看更多

    联系我们

    在线咨询: QQ交谈

    微信:13450247865

    邮件:451255340#qq.com

    工作时间:周一至周五,9:30-18:30,节假日休息

    微信