当前位置:   article > 正文

通过Redis实现防止接口重复提交功能

通过Redis实现防止接口重复提交功能

本功能是在切面执行链基础上实现的功能,如果不知道切面执行链的同学,请看一下我之前专门介绍切面执行链的文章。

在SpringBoot项目中实现切面执行链功能-CSDN博客

1.定义防重复提交handler

  1. /**
  2. * 重复提交handler
  3. *
  4. */
  5. @AspectHandlerOrder
  6. public class ResubmitAspectHandler implements AspectHandler {
  7. private StringRedisTemplate stringRedisTemplate;
  8. public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
  9. this.stringRedisTemplate = stringRedisTemplate;
  10. }
  11. @Override
  12. public boolean execute(ProceedingJoinPoint pjp) throws Exception {
  13. Method method = getMethod(pjp);
  14. if (!method.isAnnotationPresent(Resubmit.class)) {
  15. return true;
  16. }
  17. Resubmit annotation = method.getAnnotation(Resubmit.class);
  18. long ttl = annotation.ttl();
  19. String key = getKey();
  20. String value = "1";
  21. if (lock(key, value, ttl)) {
  22. return true;
  23. }
  24. throw new BaseRuntimeException(ExceptionEnums.ERROR_10012.getCode(), "操作频率过高,请稍后再试!");
  25. }
  26. @Override
  27. public void afterCompletion(ProceedingJoinPoint pjp, Object response, Exception exception) {
  28. Method method = getMethod(pjp);
  29. if (method.isAnnotationPresent(Resubmit.class)) {
  30. unlock(getKey());
  31. }
  32. }
  33. /**
  34. * redis原子操作:如果key不存在就设置key:value
  35. *
  36. * @param key
  37. * @param value
  38. * @return true:设置成功拿到锁,false:设置失败未拿到锁
  39. */
  40. private boolean lock(final String key, final String value, final long ttl) {
  41. Boolean result = stringRedisTemplate.boundValueOps(key).setIfAbsent(value, Duration.ofSeconds(ttl));
  42. return result != null ? result : false;
  43. }
  44. /**
  45. * 解锁:删除key
  46. *
  47. * @param key
  48. */
  49. private void unlock(String key) {
  50. if (StringUtils.isNotBlank(key)) {
  51. stringRedisTemplate.delete(key);
  52. }
  53. }
  54. /**
  55. * 获取方法
  56. *
  57. * @param pjp
  58. * @return
  59. */
  60. private Method getMethod(ProceedingJoinPoint pjp) {
  61. MethodSignature signature = (MethodSignature) pjp.getSignature();
  62. Method method = signature.getMethod();
  63. return method;
  64. }
  65. /**
  66. * 获取key
  67. *
  68. * @return
  69. */
  70. private String getKey() {
  71. ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  72. HttpServletRequest request = requestAttributes.getRequest();
  73. String url = request.getRequestURI();
  74. String httpMethod = request.getMethod();
  75. HttpHeader httpHeader = WebContext.getHttpHeader();
  76. String deviceId = httpHeader.getDevice_id();
  77. String key = RedisConstants.REDIS_RESUBMIT_KEY + httpMethod + url + ":" + deviceId;
  78. return key;
  79. }
  80. }

2.定义防重复提交注解

  1. /**
  2. * 防止重复提交
  3. */
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Target(ElementType.METHOD)
  6. public @interface Resubmit {
  7. /**
  8. * 存活时间(秒),当意外情况(例如锁定之后重启服务)
  9. * 未能执行解锁功能,redis将在${ttl}秒之后自动删除锁标志
  10. * 默认 10秒
  11. * @return
  12. */
  13. long ttl() default 10;
  14. }

3.在配置类中注入防重复提交切面类

  1. @Bean
  2. public List<AspectHandler> apiAspectHandlers() {
  3. ResubmitAspectHandler resubmitAspectHandler = new ResubmitAspectHandler();
  4. resubmitAspectHandler.setStringRedisTemplate(stringRedisTemplate);
  5. return Arrays.asList(resubmitAspectHandler);
  6. }

4.controller中应用防重复提交注解

  1. @PostMapping("/release")
  2. @Resubmit
  3. public ApiResponse<?> insert(@RequestBody @Valid InsertAppRequestDTO req) {
  4. // 处理业务逻辑
  5. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/684337
推荐阅读
相关标签
  

闽ICP备14008679号