千家信息网

如何使用自定义注解+Redis的拦截器实现幂等性校验

发表于:2024-11-28 作者:千家信息网编辑
千家信息网最后更新 2024年11月28日,这篇文章主要介绍"如何使用自定义注解+Redis的拦截器实现幂等性校验",在日常操作中,相信很多人在如何使用自定义注解+Redis的拦截器实现幂等性校验问题上存在疑惑,小编查阅了各式资料,整理出简单好
千家信息网最后更新 2024年11月28日如何使用自定义注解+Redis的拦截器实现幂等性校验

这篇文章主要介绍"如何使用自定义注解+Redis的拦截器实现幂等性校验",在日常操作中,相信很多人在如何使用自定义注解+Redis的拦截器实现幂等性校验问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"如何使用自定义注解+Redis的拦截器实现幂等性校验"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

实现方法

编写一个自定义注解和一个拦截器,本文中的自定义注解可以指定传入超时时间(默认是60秒),拦截器对使用注解的方法进行拦截,获取到传入的参数和超时时间,将传入的一个或多个参数拼接成一个json字符串,使用md5进行加密后把它作为key存入Redis缓存中,如果根据key在超时时间范围内能找到相同的内容,则返回表单内容已提交提示,否则继续执行方法。

自定义一个@Idempotent注解

/** * 自定义防重复提交注解 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Idempotent {    /**可以传入指定的重复提交限定时间,默认60秒*/    long value() default 60000;}

自定义一个IdempotentAspect拦截器

/** * 拦截器 * @author 豆芽 * @Date 2020-11-11 15:05 */@Aspect@Componentpublic class IdempotentAspect {    private Logger logger = LoggerFactory.getLogger(IdempotentAspect.class);    @Autowired    private RedisService redisService;    @Around("@annotation(idempotent)")    public Object aroundMethod(ProceedingJoinPoint pjp,Idempotent idempotent)throws Throwable{        /**获取执行方法的参数*/        Object[] args = pjp.getArgs();        /**获取注解传入的超时时间*/        long timeOut = idempotent.value();        /**使用MD5对传入的参数进行加密*/        String encode = getMd5Value(args);        try{            /** 校验是否重复提交过,如果没有,则按指定超时时间存入Redis缓存 */            boolean checkFormToken = redisService.checkForm(encode,timeOut);            if (checkFormToken) {                /**这是一个自定义的异常类,可以自己编写*/                throw new CommonException(Code.RepeatSubmit,"表单内容已经提交");            }            /**继续执行方法*/            return pjp.proceed();        } catch (CommonException e) {            logger.error("运行时错误:" + e.getMessage(), e);            if (Code.RepeatSubmit.getCode() != e.getCode()) {                /**调用方法可能会存在其他的CommonException自定义异常,需要删除校验的key,支持重复提交*/                redisService.delete(encode);            }            throw e;        } catch (Exception e){            logger.error("幂等性校验出错:" + e.getMessage(), e);            throw e;        }    }    /**     * 使用MD5对传入的参数进行加密     * @param args     * @return     */    private String getMd5Value(Object[] args) {        String md5 = "null";        if (args.length == 0) {            return md5;        } else {            StringBuilder jsonString = new StringBuilder(JSON.toJSONString(args));            /**使用md5工具类对字符串进行加密*/            md5 = SecureUtil.md5Encode(jsonString.toString());        }        return md5;    }}

封装的RedisService类

@Componentpublic class RedisService {    private Logger logger = LoggerFactory.getLogger(RedisService.class);    @Autowired    private StringRedisTemplate stringRedisTemplate;    /**     * 将对象存入缓存中     * @param key   key     * @param obj   对象数据     * @param timeout   超时时间     */    public void set(String key, Object obj, long timeout)    {        if (obj instanceof String)        {            stringRedisTemplate.opsForValue().set(key, (String) obj, timeout, TimeUnit.MILLISECONDS);            return;        }        String json = JSON.toJSONString(obj);        stringRedisTemplate.opsForValue().set(key, json, timeout, TimeUnit.MILLISECONDS);    }    /**     * 根据Key查询缓存中的数据     * @param key     * @return     */    public String get(final String key)    {        if (StringUtils.isEmpty(key)) {            logger.warn("获取Redis缓存,传入的Key为空");            return null;        }        return stringRedisTemplate.opsForValue().get(key);    }    /**     * 根据key删除缓存数据     * @param key     */    public void delete(String key)    {        stringRedisTemplate.delete(key);    }    /**     * 查询缓存是否存在     * @param checkCase     * @return     */    public boolean checkForm(String checkCase,long timeOut){        String cacheValue = get(checkCase);        /**如果查询缓存不为空,返回true*/        if (StringUtils.isNotEmpty(cacheValue)){            return true;        }        /**否则将对象存入缓存中,IdUtil.randomUUID()为hutool的UUID生成工具类,可到hutool官网加载相关依赖*/        set(checkCase, IdUtil.randomUUID(), timeOut);        return false;    }}

自定义的错误码枚举Code

public enum Code {    ErrorSystem(500,"系统繁忙!"),    RepeatSubmit(4,"重复提交")    ;    private int code;    private String msg;    Code(int code, String msg) {        this.code = code;        this.msg = msg;    }    public int getCode() {        return code;    }    public String getMsg() {        return msg;    }}

自定义异常类CommonException

public class CommonException extends RuntimeException {    /** 错误码 */    private int code = Code.ErrorSystem.getCode();    /** 错误信息 */    private String msg = Code.ErrorSystem.getMsg();    public CommonException(Code code) {        super("[" + code.getCode() + ":" + code.toString() +"]" + code.getMsg());        this.code = code.getCode();        this.msg = code.getMsg();    }    public CommonException(String msg) {        super("[" + Code.ErrorSystem.getCode() + ":" + Code.ErrorSystem.toString() +"]" + msg);        this.code = Code.ErrorSystem.getCode();        this.msg = msg;    }    public CommonException(Code code, String msg) {        super("[" + code.getCode() + ":" + code.toString() +"]" + msg);        this.code = code.getCode();        this.msg = msg;    }    public int getCode() {        return code;    }    public String getMsg() {        return msg;    }}

测试类RedisController,@Idempotent注解可以直接value参数,这个参数可以设置本次请求的参数在redis中的存活时间,不传参默认存活60秒

/** * @author 豆芽 * @Date 2020-11-11 11:09 */@RestControllerpublic class RedisController {    private Logger logger = LoggerFactory.getLogger(RedisController.class);    /**     * 自定义注解+Redis+MD5加密的拦截器实现幂等性校验     * @param name     * @param age     * @return     */    @Idempotent    @RequestMapping(value = "/auth/redis/idempotent",method = {RequestMethod.GET, RequestMethod.POST})    public String idempotentTest(String name,String age){        logger.info("name: "+name);        logger.info("age: "+age);                // TODO 执行保存或更新方法        return "执行成功";    }}

第一次发起请求返回执行成功

紧接着马上再发起一次相同参数的请求,系统会抛出"表单内容已经提交"的异常

到此,关于"如何使用自定义注解+Redis的拦截器实现幂等性校验"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0