Spring Aop+Redis快速实现后端api接口的幂等性设计

  • 创建自定义注解
/**
 * 防止接口重复提交
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeatSubmit {

    /**
     * 超时时间  默认为10s
     * @return
     */
    long timeout() default 10;
}
  • 引入aop依赖

       org.springframework.boot
       spring-boot-starter-aop

  • 实现切面
@Slf4j
@Aspect
@Component
public class NoRepeatSubmitAop {

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * api重复提交判断,没有考虑高并发情况
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.example.activiti.annotation.NoRepeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint)  {
        log.info("========api重复提交判断===========");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader("token");
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取自定义注解
        NoRepeatSubmit annotation = methodSignature.getMethod().getAnnotation(NoRepeatSubmit.class);
        long timeout = annotation.timeout();
        //token+请求url为key
        BoundValueOperations valueOps = redisTemplate.boundValueOps(token + request.getRequestURI());
        if (valueOps.get() != null) {
            throw new SubmitAgainException("请勿重复提交,请稍后再试!");
        }
        valueOps.set(1, timeout, TimeUnit.SECONDS);
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("接口:{}调用异常,错误信息:{},参数信息:{}",request.getRequestURL(),throwable.getMessage(), JSONUtil.toJsonStr(request.getParameterMap()));
            throw new RuntimeException("接口调用异常");
        }finally {
            redisTemplate.delete(token + request.getRequestURI());
            log.info("========api重复提交判断结束,删除key===========");
        }
        return proceed;
    }

}

实现原理:每次请求的时候会将用户token+请求的url作为key存入Redis,每次请求的时候去查询key是否存在,执行完成请求后会删除key

  • 进行测试 (请求头部得携带用户token)
    @NoRepeatSubmit
    @ApiOperation(value = "测试接口")
    @GetMapping("/success")
    public JsonResult success() throws InterruptedException {
        Thread.sleep(5000);
        return JsonResult.success();
    }

效果


11.png

你可能感兴趣的:(Spring Aop+Redis快速实现后端api接口的幂等性设计)