前提:整合好springboot + redis环境,可参考博文
springboot 整合redis-CSDN博客

0 添加aop依赖

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

1 编写自定义注解,注解的作用是标记一个方法是否支持幂等性

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 Idempotent {
    long expire() default 2L;
}

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

@Aspect
@Component
public class IdempotentAspect {

    private final RedisTemplate redisTemplate;

    @Autowired
    public IdempotentAspect(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Around("@annotation(com.example.demo.annotation.Idempotent)")
    public Object idempotent(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取请求参数
        Object[] args = joinPoint.getArgs();
        // 获取请求方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取注解信息
        Idempotent idempotent = method.getAnnotation(Idempotent.class);
        String key = getKey(joinPoint);
        // 判断是否已经请求过
        if (redisTemplate.hasKey(key)) {
            throw new RuntimeException("请勿重复提交");
        }
        // 标记请求已经处理过
        redisTemplate.opsForValue().set(key, "1", idempotent.expire(), TimeUnit.SECONDS);
        // 处理请求
        return joinPoint.proceed(args);
    }

    /**
     * 获取redis key
     */
    private String getKey(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append(className).append(":").append(methodName);
        for (Object arg : args) {
            sb.append(":").append(arg.toString());
        }
        return sb.toString();
    }
}

3 示例测试

@RestController
public class DemoController {

    @Autowired
    private TBookOrderMapper tBookOrderMapper;

    @GetMapping("/demo")
    @Idempotent(expire = 60)
    public String demo(@RequestBody TBookOrder tBookOrder) {
        // 处理请求
        int insert = tBookOrderMapper.insert(tBookOrder);
        return insert==1?"ok":"wrong";
    }
}

参考博文
SpringBoot自定义注解+AOP+redis实现防接口幂等性重复提交,从概念到实战_aop防重复提交-CSDN博客

更多推荐