一、定义一个注解用于标注需要校验重复提交的方法
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