SpringBoot项目中自定义注解实现基于AOP的防止重复提交

1. 什么是重复提交

重复提交一般是指, 在前端向后端发出请求时, 两次相同的请求在相隔较短的时间内发出了两次.

比如在登录时, 输入完账户密码后需要点击登录按钮,

假如这时有个手速很快的人, 在一瞬间连续点击了100次, 那么我们的后端就要处理100次登录请求,

很显然, 这100次登录请求中, 后面99个都是无意义的.

上面那个例子可能还不够贴切, 连续的100次登录好像也不会对我们的系统造成什么破坏,

而且, 现实中如果真有这种情况也不至于有100次.

但是,如果是新增数据的操作, 那就有大问题了.

假如我们在执行新增用户的操作时, 一不小心手抖了一下, 按了两次

那么就会有两个新增的请求发到后端,

由于是新增操作, 一般并不会区判断是否重复, 所以这两个请求都会被后台执行

虽然我们在发现问题之后可以删除, 但是很显然, 这样的系统缺陷会给我们带来不必要的麻烦,

所有我们必须要在后台识别到冲突的提交, 并且不去执行它

2. 如何防止重复提交

一般来说防止重复提交有三种方式

  • 前端页面限制
  • Interceptor拦截器
  • AOP

本文主要叙述的是第三种方法AOP

3. 主要原理

3.1 自定义注解

为了能够方便的标识出哪些方法需要用到"防止重复提交", 我们需要用到一个自定义注解.

/**
 * 用于标记防止重复提交的方法
 * @author Johnson
 */
@Inherited      // 如果一个类继承了 @Inherited 注解修饰的注解, 那么其子类也会继承这个注解
@Documented
@Retention(RetentionPolicy.RUNTIME)     // 使得该注解可以使用反射得到
@Target({ElementType.TYPE, ElementType.METHOD}) // 该注解可以对 类 和 方法 起作用
public @interface PreventDuplicateSubmit {

    /**
     * 判断重复提交到时间间隔(ms)
     * @return
     */
    int interval() default 10;
    
}

3.2 AOP切面类

首先定义切点,

我们可以很方便的使用@Pointcut注解将切点设置为所有带有@PreventDuplicateSubmit注解的方法

然后我们使用Around环绕通知来增强我们的Handler(即Controller类中的方法),

因为环绕通知可以返回我们的结果,

如果是重复提交, 就返回错误的提示信息,

如果不是, 可以返回原本的结果

/**
 * @author Johnson
 */
@Component
@Aspect
public class PreventDuplicateSubmitAspect {

    @Value("${prevent-duplicate-submit-cache}")
    private String CACHE_NAME;

    @Autowired
    private RedisCache redisCache;

    /**
     * 开启日志
     */
    private static final Logger logger = LoggerFactory.getLogger(PreventDuplicateSubmitAspect.class);

    /**
     * 定义切入点
     */
    @Pointcut("@annotation(com.ethan.annotation.PreventDuplicateSubmit)")
    public void pointcut(){};

    @Around("pointcut()")
    public Object checkRequest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        // 获取请求参数
        String args = JSON.toJSONString(proceedingJoinPoint.getArgs());
        // 获取请求 URI
        String uri = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getRequestURI();
        // 指定缓存的 key
        String cacheKey = CACHE_NAME + "_" + uri + args;
        // 获取方法
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        // 获取注解
        PreventDuplicateSubmit methodAnnotation = method.getAnnotation(PreventDuplicateSubmit.class);
        // 判断是否存在缓存, 如果存在则为重复提交, 否则添加到缓存
        Boolean isDuplicateSubmit = redisCache.isCaught(cacheKey, methodAnnotation.interval(), TimeUnit.SECONDS);
        // isDuplicateSubmit 为true则证明没有重复提交
        if(isDuplicateSubmit){
            throw  new RuntimeException("请勿重复提交");
        }
        return proceedingJoinPoint.proceed();
    }
}

3.3 Redis缓存

在AOP的代码中, 我们使用了一个Redis缓存的工具类RedisCatch, 具体代码如下

我们直接使用RedisTemplate这个API, 帮助我查询缓存中是否包含本次的请求, 如果包含,那么重置一下缓存的有效期

如果没有就将请求添加到缓存.

/**
 * spring redis 工具类
 *
 **/
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;


    public Boolean isCaught(String key, int interval, TimeUnit timeUnit) {
        Object object = redisTemplate.opsForValue().get(key);
        if (object != null) {
            redisTemplate.expire(key, interval, timeUnit);
            return true;
        }
        redisTemplate.opsForValue().set(key, "", interval, timeUnit);
        return false;
    }
}

至此, 我们代码的核心内容就结束了,

我们使用切面类代理我们的Controller中的方法, 当方法执行时, 我们先在redis中检查一下是否已经有过这个请求

有 -> 是重复提交 -> 重置缓存有效期 -> 返回错误信息

没有 -> 不是重复信息 -> 添加到缓存 -> 返回正确内容

4. 源代码

gitee仓库地址

prevent_duplicate_submit_demo: SpringBoot 自定义注解 防止重复提交 AOP方式 (gitee.com)

最后附上我的源代码连接, 欢迎各位网友阅读使用,

如果您有改进意见, 欢迎评论

  • 32
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot使用AOP自定义权限注解可以通过以下步骤实现: 1. 首先,在pom.xml文件添加Spring Boot AOP的依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` \[1\] 2. 创建一个自定义的注解,用于标记需要进行权限控制的方法或类。例如,可以创建一个名为@CustomPermission的注解。 3. 创建一个切面类,用于定义权限控制的逻辑。在切面类,可以使用@Before、@After、@Around等注解来定义在方法执行前、执行后或者环绕方法执行时需要执行的逻辑。在这个切面类,可以通过获取方法的参数、注解等信息来进行权限校验和控制。 4. 在Spring Boot的配置类,使用@EnableAspectJAutoProxy注解来启用AOP功能。 5. 在需要进行权限控制的方法或类上,添加自定义的权限注解@CustomPermission。 通过以上步骤,就可以在Spring Boot使用AOP自定义权限注解实现权限控制了。使用AOP可以更加灵活地对方法进行拦截和处理,同时可以通过自定义注解来标记需要进行权限控制的方法或类。\[2\]\[3\] #### 引用[.reference_title] - *1* [springboot+mybatis+aop+注解实现数据权限](https://blog.csdn.net/weixin_42935902/article/details/116758260)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [springboot+自定义注解+AOP实现权限控制(一)](https://blog.csdn.net/byteArr/article/details/103984725)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值