当前位置: 首页 > article >正文

使用SpringBoot自定义注解+AOP+redisson锁来实现防接口幂等性重复提交

前提,整合好springboot和redis,redisson的环境

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2 编写自定义注解,注解的作用是标记一个方法是否支持防重提交

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotRepeatDuplication {
    
    /**
     * 延时时间 在延时多久后可以再次提交,默认10秒
     * @return 秒
     */
    int delaySeconds() default 10;
    
}

AOP切面逻辑,来判断一个方法是否被标记了该注解。如果被标记了该注解,那么就需要对该方法进行特殊处理,以实现幂等性

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * 使用Redisson分布式锁来防止接口重复调用的AOP切面类
 */
@Component
@Aspect
@Slf4j
public class LockMethodAOP {
    
    @Autowired
    private RedissonClient redissonClient;
    
    /**
     * @param pjp:                  切入点对象,包含被拦截方法的信息
     * @param notRepeatDuplication: 自定义注解,用于指定防重复提交的延迟时间
     * @throws Throwable: 抛出异常时重新抛出
     * @description: 环绕通知方法,用于在方法执行前获取分布式锁,在方法执行后释放锁
     * @return: 执行被拦截方法的返回值
     */
    @Around("execution(* com.example.controler..*.*(..))  && @annotation(notRepeatDuplication)  ")
    public Object interceptor(ProceedingJoinPoint pjp, NotRepeatDuplication notRepeatDuplication) throws Throwable {
        // 生成锁的唯一键
        String lockKey = generateKey(pjp);
        // 获取Redisson分布式锁
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取锁,如果在指定时间内未获取到锁,则抛出异常表示重复提交
            boolean b = lock.tryLock(0, notRepeatDuplication.delaySeconds(), TimeUnit.SECONDS);
            if (!b) {
                throw new RuntimeException("请忽重复提交");
            }
            // 执行被拦截的方法
            return pjp.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * @param pjp: 切入点对象,包含被拦截方法的信息
     * @description: 生成锁的唯一键
     * @return: MD5加密后的字符串,用作锁的唯一键
     */
    private String generateKey(ProceedingJoinPoint pjp) {
        /**
         * 取得类名,方法名,参数
         */
        String className = pjp.getTarget().getClass().getName();
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        
        StringBuilder sb = new StringBuilder();
        sb.append(className).append(":").append(methodName);
        for (Object arg : args) {
            sb.append(":").append(arg.toString());
        }
        
        log.info("锁的唯一键:{}",sb.toString());
        // 使用MD5加密生成的字符串,确保键的唯一性
        String s = DigestUtils.md5DigestAsHex(sb.toString().getBytes(StandardCharsets.UTF_8));
        log.info("锁的唯一键:{}",s);
        return s;
    }
}

4 测试,需要防重提交的方法,加上注解

@RestController
public class DemoController {
 
    @Autowired
    private TBookOrderMapper tBookOrderMapper;
 
    @GetMapping("/demo")
    @NotRepeatDuplication(delaySeconds = 10)
    public String demo(@RequestBody TBookOrder tBookOrder) {
        // 处理请求
        int insert = tBookOrderMapper.insert(tBookOrder);
        return insert==1?"ok":"wrong";
    }
}


http://www.kler.cn/news/360832.html

相关文章:

  • 加密,混淆,摘要,序列化的理解
  • 柔性数组的使用
  • 基于ECS和NAS搭建个人网盘
  • Java访问修饰符private,default,protected,public
  • LeetCode_2413. 最小偶倍数_java
  • 基于Multisim的任意进制计数器设计与仿真
  • 【Linux 从基础到进阶】磁盘I/O性能调优
  • uniapp 实现input聚焦时选中内容(已封装)兼容微信小程序
  • uniapp的相关知识(2)
  • 10.22学习
  • Recall/Coarse Ranking/Fine Ranking
  • 每日算法一练:剑指offer——数组篇(3)
  • 图像异常检测评估指标-分类性能
  • 存-20241019 CSPJ模拟测试5 题解
  • 免费开源的微信开发框架
  • CentOS安装Docker教程,及报错。
  • RK3568平台开发系列讲解(调试篇)debugfs 文件系统
  • C++中static关键字的用法(实现日期类)
  • 【Vulnhub靶场】Kioptrix Level 5
  • 【算法】深入了解 CRC 校验码的计算过程