重复提交是我们日常中比较频繁发生的事情,用户可以点击一个按钮多次,如未加相应的处理,就会请求后台多次,每次请求对后台服务来说都是一次性能的消耗,有些非幂等接口更是会有很多的业务问题,为此解决重复提交是一件非常重要的事情。当然解决重复提交也有很多种方法,如接口幂等,redis,数据库,mq等等,现讲解Spring + redis的方案解决重复提交,根据每次的请求路径+用户的请求sessionId来判别是否是同一个用户,通过判断用户请求的key是否在redis中存在来判别是否失效,话不多说,show me your code!
/** * @version V1.0 * @Description 防止重复提交 * @date 2020/7/15 19:39. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface NoRepeatSubmit { /** * 设置请求锁定时间 * * @return */ int lockTime() default 3; }
/** * @version V1.0 * @Description 防止重复提交 * @date 2020/7/15 19:46. */ @Slf4j @Aspect @Component public class RepeatSubmitAspect { @Autowired private RedisTemplate<String, Integer> redisTemplate; @Pointcut("@annotation(noRepeatSubmit)") public void pointCut(NoRepeatSubmit noRepeatSubmit) { } @Around("pointCut(noRepeatSubmit)") public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) { ValueOperations<String, Integer> opsForValue = redisTemplate.opsForValue(); try { int lockSeconds = noRepeatSubmit.lockTime(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); String sessionId = RequestContextHolder.getRequestAttributes().getSessionId(); HttpServletRequest request = attributes.getRequest(); String key = sessionId + ":" + request.getServletPath(); // 如果缓存中有这个url视为重复提交 if (opsForValue.get(key) == null) { Object o = pjp.proceed(); opsForValue.set(key, 0, lockSeconds, TimeUnit.SECONDS); return o; } else { log.info("重复提交"); return RtnUtil.getFailMsgRtn("请勿重复提交~"); } } catch (Throwable e) { e.printStackTrace(); log.error("验证重复提交时出现未知异常!"); return RtnUtil.getOkMsgRtn("验证重复提交时出现未知异常!"); } } }
@NoRepeatSubmit(lockTime = 5)
/** * @version V1.0
* @Description 描述
* @date 2020/7/21 12:52.
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLog {
/**
* 业务名称
* @return
*/
String businessName() default "操作日志";
/**
* 业务类型
* @return
*/
String businessType() default "system";
}
/** * @version V1.0 * @Description 描述 * @date 2020/7/21 12:53. */ @Aspect @Component public class OperateLogAspect { @Pointcut(value = "@annotation(operateLog) && args(obj)") public void pointCut(OperateLog operateLog, Object obj) { } @AfterThrowing(pointcut = "pointCut(operateLog,obj)", throwing = "ex") public void afterThrowing(OperateLog operateLog, BusinessException ex, Object obj) { if (operateLog.businessType().equalsIgnoreCase("operator:bussiness")) { ParamObj paramObj = (ParamObj) obj; System.out.println("save::" + paramObj.toString()); } } }
原文:https://www.cnblogs.com/antonyhubei/p/13357040.html