spring框架中-自定义@NoRepeatSubmit防止重复提交

为了防止用户重复快速提交会导致后台接收到请求多次,产生数据问题甚至报错,因此后端也要做一个防止重复提交的限制。

首先自定义接口@NoRepeatSubmit类:

import java.lang.annotation.*;

/**
 * @desc    定义一个不重复提交的注解
 * @author  lq
 * @create  2022年04月22日15:55:07
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeatSubmit {


    /**
     * 防止重复参数默认值
     * @return
     */
    String value() default "";

    /**
     * 过期时间
     * @return
     */
    int milliseconds()  default 2;
} 

自定义缓存类:


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Map 缓存实现
 */
public class MapCache {

    /**
     * 默认存储1024个缓存
     */
    private static final int DEFAULT_CACHES = 1024;

    private static final MapCache INS = new MapCache();

    public static MapCache single() {
        return INS;
    }

    /**
     * 缓存容器
     */
    private static Map<String, CacheObject> cachePool;

    public MapCache() {
        this(DEFAULT_CACHES);
    }

    public MapCache(int cacheCount) {
        cachePool = new ConcurrentHashMap<>(cacheCount);
    }

    /**
     * 读取一个缓存
     *
     * @param key 缓存key
     * @param <T>
     * @return
     */
    public static <T> T get(String key) {
        CacheObject cacheObject = cachePool.get(key);
        if (null != cacheObject) {
            long cur = System.currentTimeMillis() / 1000;
            if (cacheObject.getExpired() <= 0 || cacheObject.getExpired() > cur) {
                Object result = cacheObject.getValue();
                return (T) result;
            }
        }
        return null;
    }

    /**
     * 读取一个hash类型缓存
     *
     * @param key   缓存key
     * @param field 缓存field
     * @param <T>
     * @return
     */
    public <T> T hget(String key, String field) {
        key = key + ":" + field;
        return this.get(key);
    }

    /**
     * 设置一个缓存
     *
     * @param key   缓存key
     * @param value 缓存value
     */
    public static void set(String key, Object value) {
        set(key, value, -1);
    }

    /**
     * 设置一个缓存并带过期时间
     *
     * @param key     缓存key
     * @param value   缓存value
     * @param expired 过期时间,单位为秒
     */
    public static void set(String key, Object value, long expired) {
        expired = expired > 0 ? System.currentTimeMillis() / 1000 + expired : expired;
        CacheObject cacheObject = new CacheObject(key, value, expired);
        cachePool.put(key, cacheObject);
    }

    /**
     * 设置一个hash缓存
     *
     * @param key   缓存key
     * @param field 缓存field
     * @param value 缓存value
     */
    public void hset(String key, String field, Object value) {
        this.hset(key, field, value, -1);
    }

    /**
     * 设置一个hash缓存并带过期时间
     *
     * @param key     缓存key
     * @param field   缓存field
     * @param value   缓存value
     * @param expired 过期时间,单位为秒
     */
    public void hset(String key, String field, Object value, long expired) {
        key = key + ":" + field;
        expired = expired > 0 ? System.currentTimeMillis() / 1000 + expired : expired;
        CacheObject cacheObject = new CacheObject(key, value, expired);
        cachePool.put(key, cacheObject);
    }

    /**
     * 根据key删除缓存
     *
     * @param key 缓存key
     */
    public static void del(String key) {
        cachePool.remove(key);
    }

    /**
     * 根据key和field删除缓存
     *
     * @param key   缓存key
     * @param field 缓存field
     */
    public void hdel(String key, String field) {
        key = key + ":" + field;
        this.del(key);
    }

    /**
     * 清空缓存
     */
    public void clean() {
        cachePool.clear();
    }

    static class CacheObject {
        private String key;
        private Object value;
        private long expired;

        public CacheObject(String key, Object value, long expired) {
            this.key = key;
            this.value = value;
            this.expired = expired;
        }

        public String getKey() {
            return key;
        }

        public Object getValue() {
            return value;
        }

        public long getExpired() {
            return expired;
        }
    }

}

接口重复提交处理类:


import com.haileer.dd.dingdingserver.entity.Results;
import com.haileer.dd.dingdingserver.utils.MapCache;
import lombok.extern.log4j.Log4j2;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * @author  lq
 * @create  2022年04月22日15:59:07
 */
@Aspect
@Component
@Log4j2
public class RepeatSubmitAspect {

    @Pointcut("@annotation(NoRepeatSubmit)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp) throws BusinessException {
        Object result = null;
        StringBuilder sb = new StringBuilder();


        // 拦截的实体类
        Object target = pjp.getTarget();
        //获取类名
        String className = target.getClass().getName();

        sb.append(className);


        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();

        //获得注解
        NoRepeatSubmit noRepeatSubmit = method.getAnnotation(NoRepeatSubmit.class);


        // 拦截的方法名称。当前正在执行的方法
        String methodName = method.getName();

        sb.append(methodName);

        // 拦截的方法参数
        Object[] args = pjp.getArgs();


        for (Object arg : args) {
            if (arg == null) {
                continue;
            }
            int code = arg.hashCode();
            sb.append("#");
            sb.append(code);
        }


        String value = noRepeatSubmit.value();
        if (StringUtils.isEmpty(value)) {
            value = sb.toString();
        }

     //获取过期时间
        try {
            Object count = MapCache.get(value);
            MapCache.set(value, 1,2);
            if (count != null) {
                System.out.println("-----------------------------count="+count);
                //重复提交
                return new Results(201, "不可重复提交");
            }
            
        } catch (Exception e) {
            MapCache.del(value);
            log.error("redis加锁异常", e);
            throw new BusinessException(e.getMessage());
        }


        try {
            //执行原来方法
            result = pjp.proceed();
        } catch (Throwable e) {
            log.error("分布式防重复操作异常Throwable::" + e.getMessage());
            e.printStackTrace();
            throw new BusinessException(e.getMessage());
        }
        return result;
    }

自定义异常类:

public class BusinessException extends Exception {

    public BusinessException(String message){
        super(message);
    }

}

然后就是使用,只需要在接口上面新增@NoRepeatSubmit这个注释就可以:

可以自行测试哦~

相关推荐

  1. Spring Cloud项目如何防止重复提交定义注解)

    2024-04-30 15:04:04       64 阅读
  2. SpringBoot防止重复提交 AOP+定义注解+redis

    2024-04-30 15:04:04       27 阅读
  3. Spring定义注解+AOP实现接口防重复提交

    2024-04-30 15:04:04       39 阅读
  4. 模拟防止重复提交

    2024-04-30 15:04:04       23 阅读
  5. uniapp 防止重复提交数据

    2024-04-30 15:04:04       24 阅读
  6. 分布式防止重复请求或者高并发防止重复提交

    2024-04-30 15:04:04       21 阅读
  7. 后端怎样防止重复提交订单?

    2024-04-30 15:04:04       56 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-30 15:04:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-30 15:04:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-30 15:04:04       82 阅读
  4. Python语言-面向对象

    2024-04-30 15:04:04       91 阅读

热门阅读

  1. bind、call和apply

    2024-04-30 15:04:04       37 阅读
  2. Agent AI智能体的未来

    2024-04-30 15:04:04       35 阅读
  3. 2024-04-30 区块链-timi.net-大陆员工办公规定-记录

    2024-04-30 15:04:04       35 阅读
  4. 前端开发中可能出现内存泄漏的情况总结

    2024-04-30 15:04:04       36 阅读
  5. OceanBase的SQL 优化实践: NOT IN 子查询

    2024-04-30 15:04:04       25 阅读
  6. 微信小程序 图片上传到文件服务器

    2024-04-30 15:04:04       27 阅读
  7. 吐槽某为的招聘是最恶心的招聘,没有之一

    2024-04-30 15:04:04       32 阅读
  8. 使用 Lua 协程模拟 Golang 的 go defer 编程模式

    2024-04-30 15:04:04       34 阅读
  9. React 模板选择标准

    2024-04-30 15:04:04       32 阅读