使用aop防止重复提交
在开发期间,我们总会遇到重复提交的操作,比如: 下单时用户手抖,有人故意频繁的请求某个api,这种情况下除了前端进行处理,
后端也需要处理的,这里使用的是aop的方式,也可以使用redis分布式锁来实现,这个自行上网查找哈!
实现方法: 通过注解类找到对应的切面类,根据存储用户的唯一标识(用户id)到缓存中来限制它的重复提交。
注解类:
import java.lang.annotation.*;
/**
* 防止用户下单重复提交注解类
*
* @author serence
* @date 2021/11/7 20:49
*/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PlaceAnOrderRepeatSubmit {
String name() default "name:";
}
切面类
/**
* 切入点
* <p>
* execution: 使用execution给批量方法添加切面, 定义的包名要在注解类的上一级
*
* @param point 连接点
* @param nrs 防止重复提交
* @return
* @throws Throwable
*/
@Around(value = "execution(* com.serene..*.*(..)) && @annotation(nrs)")
public Object placeAnOrderAround(ProceedingJoinPoint point, PlaceAnOrderRepeatSubmit nrs) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
try {
//访问目标方法的参数:
Object[] args = point.getArgs();
if (args != null && args.length > 0) {
//获取参数对象
Orders or = (Orders) point.getArgs()[0];
Long memberId = new Long(AESEncryptUtils.decrypt(or.getEncryptionId()));
//获取请求路径url
String key = memberId + "-" + request.getServletPath();
// 如果缓存中有这个url视为重复提交, 否则通过执行存储key
if (!redisService.haskey(key)) {
System.out.println("获取 入参前的参数:" + point.getArgs()[0]);
String str = Arrays.toString(point.getArgs());
//执行目标方法
Object ob = point.proceed();
System.out.println("获取 调用方法:" + point.getSignature().getName());
System.out.println("获取 目标对象:" + point.getTarget());
System.out.println("获取 入参后的参数:" + point.getArgs()[0]);
Orders entity = (Orders) point.getArgs()[0];
System.out.println("获取 对象实体: " + entity);
redisService.setCacheObject(key, 0, 3, TimeUnit.SECONDS);
return ob;
} else {
log.info("请勿重复提交");
//请求了同样的url继续限制
redisService.setCacheObject(key, 0, 3, TimeUnit.SECONDS);
return AjaxResult.error("请勿重复提交或者操作过于频繁");
}
}
} catch (Exception e) {
e.printStackTrace();
}
throw new CustomException("系统开了点小差,请稍后重试");
}
这里定义3秒,这里的缓存key: 用户唯一标识+api请求url
然后在你控制器上自定义注解就好了
@PlaceAnOrderRepeatSubmit
@GetMapping("test")
public AjaxResult test(){
return AjaxResult.success("成功");
}
redis存储代码如下:
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
* @return 缓存的对象
*/
public <T> ValueOperations<String, T> setCacheObjectAop(String key, T value, Integer timeout, TimeUnit timeUnit) {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
operation.set(key, value, timeout, timeUnit);
return operation;
}
/**
* 获取键值数据
*
* @param key 键值
* @return
*/
public boolean haskey(String key) {
return redisTemplate.hasKey(key);
}