一、定义一个注解用于标注需要校验重复提交的方法
package com.xwolf.boot.annotation; import java.lang.annotation.*; /** * 避免重复提交 * @author xwolf * @version 1.0 * @since 1.8 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AvoidRepeatableCommit { /** * 指定时间内不可重复提交,单位毫秒 * @return */ long timeout() default 30000 ; }
二、添加一个切面用于校验来自同一IP地址的重复提交请求
package com.xwolf.boot.aspect; import com.xwolf.boot.annotation.AvoidRepeatableCommit; import com.xwolf.boot.config.Constants; import com.xwolf.boot.utils.IPUtil; import com.xwolf.boot.utils.StringUtils; import com.xwolf.boot.utils.UUIDUtil; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; /** * 重复提交aop * @author xwolf * @version 1.0 * @since 1.8 */ @Order @Aspect @Component @Slf4j public class AvoidRepeatableCommitAspect { @Autowired private RedisTemplate redisTemplate; /** * @param point */ @Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)") public Object around(ProceedingJoinPoint point) throws Throwable { HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest(); String ip = IPUtil.getIP(request); //获取注解 MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); //目标类、方法 String className = method.getDeclaringClass().getName(); String name = method.getName(); String ipKey = String.format("%s#%s",className,name); int hashCode = Math.abs(ipKey.hashCode()); String key = String.format("%s_%d",ip,hashCode); log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key); AvoidRepeatableCommit avoidRepeatableCommit = method.getAnnotation(AvoidRepeatableCommit.class); long timeout = avoidRepeatableCommit.timeout(); if (timeout < 0){ timeout = Constants.AVOID_REPEATABLE_TIMEOUT; } String value = (String) redisTemplate.opsForValue().get(key); if (StringUtils.isNotBlank(value)){ return "请勿重复提交"; } redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS); //执行方法 Object object = point.proceed(); return object; } }
三、对需要检验重复提交的方法添加注解
/** * 添加用户 * @param user * @return */ @PostMapping(value = "add") @AvoidRepeatableCommit(timeout = 50000) public String insert(@Valid User user){ // user.setBirth(new Date()); log.info("请求参数:{}",user); return userService.insert(user); }
方案出自:https://github.com/fkandy/boot/tree/master/src/main/java/com/xwolf/boot