java后端如何实现防止接口重复提交
作者:是小故事呀
这篇文章主要介绍了java后端如何实现防止接口重复提交问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
java后端防止接口重复提交
利用redis实现防止前端重复点击按钮重复提交。
解释:
此方法是利用AOP+Redis实现防止接口重复请求
在需要防止重复提交的方法上,添加CheckRepeatCommit 注解。
此注解额,有两个属性,channel属性是当前访问的系统,redis中key的前缀。
可不填。
expireTime 属性,是添加了此注解的方法多久之内不允许同一用户重复请求,默认3秒。
防止重复提交的原理就是:
在每次请求加了CheckRepeatCommit注解的接口时,都会利用AOP在redis中保存一个从1开始的自增数字,并设置此KEY的过期时间,当后续同一个用户再次请求时,判断此自增数字是否已存在并>=1,如果已存在并>=1,抛出异常。
话不多说,直接上代码
自定义注解
import java.lang.annotation.*; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CheckRepeatCommit { String channel() default "APP"; int expireTime() default 3; }
定义切面
import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; @Component @Aspect @Slf4j public class CheckRepeatCommitAspect { @Autowired private RedisTemplate<String, String> redisTemplate; @Pointcut("@annotation(com.upbim.twin.park.common.annotation.CheckRepeatCommit)") private void checkRepeatCommit() { } @Around("checkRepeatCommit()") public Object checkRepeatCommit(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); Object target = joinPoint.getTarget(); //得到拦截的方法 Method method = getMethodByClassAndName(target.getClass(), methodName); log.info("验证是否重复提交:" + "调用方法:" + method); //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String channel = ""; String bizKey = method.getName(); int expireTime = 0; // 获取当前请求方法的注解,根据注解配置获取参数 CheckRepeatCommit checkRepeatCommit = method.getAnnotation(CheckRepeatCommit.class); String userNo = SysUserContextHolder.getSysUser().getUserNo(); String key; if (checkRepeatCommit != null) { //注解上的描述 channel = checkRepeatCommit.channel(); expireTime = checkRepeatCommit.expireTime(); key = getRepeatCommitLock(channel, className, bizKey, userNo, expireTime); if (StringUtils.isBlank(key)) { throw new SystemException(ResultEnum.RE_COMMIT); } } return joinPoint.proceed(); } /** * 根据类和方法名得到方法 */ public Method getMethodByClassAndName(Class c, String methodName) { Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { return method; } } return null; } public String getRepeatCommitLock(String channel, String module, String bizKey, String userNo, int expireTime) { if (StringUtils.isEmpty(module) || StringUtils.isEmpty(bizKey)) { throw new ParamException(ResultEnum.PARAMETER_CHECK_ERROR, "getRepeatCommitLock{} 参数不能为空!"); } String redisKey = channel + StrUtil.COLON + module + StrUtil.COLON + bizKey + StrUtil.COLON + userNo; long count = redisTemplate.opsForValue().increment(redisKey, 1); if (count == 1) { if (expireTime == 0) { expireTime = 60; } redisTemplate.expire(redisKey, expireTime, TimeUnit.SECONDS); return redisKey; } else { return null; } } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。