使用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);
    }

 

posted @ 2021-11-08 21:12  安详的苦丁茶  阅读(221)  评论(0编辑  收藏  举报