后台防止重复点击的实现方案比较多,这里介绍的是利用Spring的Aspect来实现接口防重点击。话 不多说,直接附上代码
1、配置自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 重复点击的切面
*
* @author Jonathan.WQ
* @date 2021年05月14日 15:03
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {
/**
* 锁过期的时间
*/
int seconds() default 5;
/**
* 锁的位置
*/
String location() default "NoRepeatSubmit";
/**
* 要扫描的参数位置
*/
int argIndex() default 0;
/**
* 参数名称
*/
String name() default "";
/**
* 提示消息
*/
String message() default "";
}
2、实现防重复点击的切面
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.Map;
/**
* 防重复点击的切面
*/
@Aspect
@Component
public class NoRepeatSubmitAspect {
private static final Logger logger = LoggerFactory.getLogger(NoRepeatSubmitAspect.class);
private static final String SUFFIX = "SUFFIX";
@Autowired
private IRedisService redisService;
/**
* 横切点
*/
@Pointcut("@annotation(noRepeatSubmit)")
public void repeatPoint(NoRepeatSubmit noRepeatSubmit) {
}
/**
* 接收请求,并记录数据
*/
@Around(value = "repeatPoint(noRepeatSubmit)")
public Object doBefore(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) {
String key = RedisKey.NO_REPEAT_LOCK_PREFIX + noRepeatSubmit.location();
Object[] args = joinPoint.getArgs();
String name = noRepeatSubmit.name();
int argIndex = noRepeatSubmit.argIndex();
String message = noRepeatSubmit.message();
String suffix;
if (StringUtils.isEmpty(name)) {
suffix = String.valueOf(args[argIndex]);
} else {
Map<String, Object> keyAndValue = getKeyAndValue(args[argIndex]);
Object valueObj = keyAndValue.get(name);
if (valueObj == null) {
suffix = SUFFIX;
} else {
suffix = String.valueOf(valueObj);
}
}
key = key + ":" + suffix;
logger.info("==================================================");
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof HttpServletRequest
|| args[i] instanceof HttpServletResponse) {
continue;
}
logger.info(JSON.toJSONString(args[i]));
}
logger.info("==================================================");
int seconds = noRepeatSubmit.seconds();
logger.info("lock key : " + key);
Object obj = redisService.get(key);
if (StringUtils.isNotNull(obj)) {
return new CommonResult().failed(StringUtils.isNotNull(message) ? message : "操作过于频繁,请稍后重试");
}
try {
Object proceed = joinPoint.proceed();
return proceed;
} catch (Throwable throwable) {
logger.error("运行业务代码出错", throwable);
throw new RuntimeException(throwable.getMessage());
} finally {
redisService.setExpire(key, "1", seconds);
}
}
public static Map<String, Object> getKeyAndValue(Object obj) {
Map<String, Object> map = Maps.newHashMap();
// 得到类对象
Class userCla = (Class) obj.getClass();
/* 得到类中的所有属性集合 */
Field[] fs = userCla.getDeclaredFields();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
// 设置些属性是可以访问的
f.setAccessible(true);
try {
Object val = f.get(obj);
// 得到此属性的值
// 设置键值
map.put(f.getName(), val);
} catch (IllegalArgumentException e) {
logger.error("getKeyAndValue IllegalArgumentException", e);
} catch (IllegalAccessException e) {
logger.error("getKeyAndValue IllegalAccessException", e);
}
}
logger.info("扫描结果:" + JSON.toJSON(map));
return map;
}
}
/**
* 锁前缀类
*
* @author Jonathan.WQ
* @date 2021年05月25日 19:47
*/
public class RedisKey {
/**
* 不可重复点击的锁前缀
*/
public static final String NO_REPEAT_LOCK_PREFIX = "jkzs:no_repeat_lock:";
}
3.使用自定义注解
将编写好的自定义注解添加到控制器类上,根据需要调整各个参数
@Autowired
private IMiniFeedbackService miniFeedbackService;
@ApiOperation("新增意见反馈")
@OperateLogAnnotation(remark = "新增意见反馈", operationType = OperationType.INSERT)
@PostMapping("/add")
@NoRepeatSubmit(location = "feedback:add", message = "您已提交过反馈,请勿重复提交!", seconds = 15)
public CommonResult addHandler(@RequestParam(required = false) String content, @RequestParam(required = false) String contactPhone, @RequestParam(required = false) MultipartFile picture, HttpServletRequest request) {
return miniFeedbackService.add(content, contactPhone, picture, request);
}