• 请求防重处理


    1、自定义注解

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public  @interface NoRepeatSubmit {
    
        /**
         * 默认1s钟以内算重复提交
         * @return
         */
        int lockTime() default 1000;
    }

    2、定义切面类

    /**
     * 接口重复调用切面
     * 仅使用单机时
     */
    @Aspect
    @Component
    @Slf4j
    public class RepeatSubmitAspect {
    
         //定义切入点表达式
        @Pointcut("@annotation(noRepeatSubmit)")
        public void pointCut(NoRepeatSubmit noRepeatSubmit) {
        }
    
        @Around("pointCut(noRepeatSubmit)")
        public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {
            int lockMillisecond = noRepeatSubmit.lockTime();
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            Assert.notNull(request, "request can not null");
            //此处可以用或者JSessionId
            String token = request.getHeader("Cookie");
            String path = getPath(request);
            Object[] args = pjp.getArgs();
            String key = getKey(token, path, args);
            boolean isSuccess = LocalCache.getInstance().tryLock(key, lockMillisecond);
            log.info("获取锁 key = [{}], clientId = [{}]", key,token);
    
            if (isSuccess) {
                log.info("获取锁 成功, key = [{}], clientId = [{}]", key,token);
                // 获取锁成功
                Object result;
                try {
                    // 执行进程
                    result = pjp.proceed();
                } finally {
                    // 解锁
                    LocalCache.getInstance().releaseLock(key);
                    log.info("释放锁 成功, key = [{}], clientId = [{}]", key,token);
                }
                return result;
            } else {
                // 获取锁失败,认为是重复提交的请求
                log.info("获取锁 失败, key = [{}]", key);
                LayUiResult result = new LayUiResult();
                result.setResultStr("操作太快了,请稍后重试");
                result.setResult(false);
                return result;
            }
    
        }
    
        private String getKey(String token, String path, Object... args) {
            return token + "_" + path + "_" + getParam(args);
        }
    
        public <T> String getNotNullFieldStr(T origin) {
            if (origin == null)
                return "";
            StringBuffer stringBuffer = new StringBuffer();
            Field[] fields = origin.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                try {
                    fields[i].setAccessible(true);
                    String name = fields[i].getName();
                    Object value = fields[i].get(origin);
                    if (null != value) {
                        stringBuffer.append(name).append("=").append(value).append("&");
                    }
                    fields[i].setAccessible(false);
                } catch (Exception e) {
                }
            }
            return stringBuffer.toString();
        }
    
        public static boolean isWrapClass(Class clz) {
            try {
                return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
            } catch (Exception e) {
                return false;
            }
        }
    
        /**
         * 生成md5
         *
         * @param base
         * @return
         */
        public static String getMD5(String base) {
            String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
            return md5;
        }
    
        private String getParam(Object[] args) {
            if (args == null) {
                return "";
            }
            StringBuffer stringBuffer = new StringBuffer();
            for (Object object : args) {
                if (object != null) {
                    if (isWrapClass(object.getClass())) {
                        stringBuffer.append(object.toString());
                    } else {
                        stringBuffer.append(getMD5(getNotNullFieldStr(object)));
                    }
                }
            }
            return getMD5(stringBuffer.toString());
        }
    
        private String getPath(HttpServletRequest request) {
            String uri = request.getRequestURI();
            String contextPath = request.getContextPath();
            if (StringUtils.length(contextPath) > 0) {
                uri = StringUtils.substring(uri, contextPath.length());
            }
            return uri;
        }
    }

    3、本地接口调用内存缓存

    package com.idea.aop;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 本地接口调用内存缓存
     */
    @Slf4j
    public class LocalCache {
        private static LocalCache instance = new LocalCache();
    
        private LocalCache() {
    
        }
    
        public static LocalCache getInstance() {
            return instance;
        }
    
        /**
         * {key:{EXPIRED:时间戳,INSERT:时间戳},...}
         */
        private Map<String, Map<String, Object>> localMap = new HashMap<>();
    
        /**
         * 获取锁,当key存在且未过期时可以成功获得锁
         *
         * @param key
         * @param lockMillisecond
         * @return
         */
        public synchronized boolean tryLock(String key, long lockMillisecond) {
            //清理缓存
            clearExpired();
            //key已存在
            if (localMap.containsKey(key)) {
                //已过期
                if (expired(key, lockMillisecond)) {
                    //已过期,从Map中移除
                    localMap.remove(key);
                    addCache(key, lockMillisecond);
                    return true;
                } else {
                    //未过期
                    return false;
                }
            } else {
                //未在执行中
                addCache(key, lockMillisecond);
                return true;
            }
        }
    
        private void addCache(String key, long lockMillisecond) {
            Map map = new HashMap<>();
            map.put(EXPIRED, lockMillisecond);
            map.put(INSERT, new Date().getTime());
            localMap.put(key, map);
        }
    
        /**
         * 过期时间_KEY
         */
        /**
         * 创建时间_KEY
         */
        private static final String EXPIRED = "EXPIRED";
        private static final String INSERT = "INSERT";
    
        /**
         * 是否已过期
         *
         * @param key
         * @param lockMillisecond
         * @return
         */
        private synchronized boolean expired(Object key, long lockMillisecond) {
            Map<String, Object> map = localMap.get(key);
            //不存在 默认返回未过期
            if (map == null) {
                return true;
            }
            //数据接口调用时间戳
            long oldTime = (Long) map.get(INSERT);
    
            long now = new Date().getTime();
    
            //已过期:当前时间戳-(开始时间+过期时间)大于 0
            return (now - (oldTime + lockMillisecond)) > 0L;
        }
    
        /**
         * 清理Map中所有已过期的接口请求
         */
        private synchronized void clearExpired() {
            for (Map.Entry entry : localMap.entrySet()) {
                Object key = entry.getKey();
                Map<String, Object> map = localMap.get(key);
                long expired = (Long) map.get(EXPIRED);
                if (expired(key, expired)) {
                    releaseLock(key);
                }
            }
        }
    
        /**
         * 释放锁
         *
         * @param key
         */
        public synchronized void releaseLock(Object key) {
            localMap.remove(key);
            log.info("本地缓存释放,Key:{}", key);
        }
    
    }

     知识点回顾:

    request.getRequestURL() 返回全路径
    request.getRequestURI() 返回除去host(域名或者ip)部分的路径
    举例如下:
    request.getRequestURL()  http://localhost:8080/project_name/user/login.do
    request.getRequestURI()  /project_name/user/login.do
    request.getServletPath()    这个方法获取的是包括servlet之后的路径,不包括项目名的路径  /user/login.do
    request.getContextPath()  从字面我们知道contextpath的意思是容器的路径,我们可以把context理解为项目。所以这个方法获取的就是项目名路径  /project_name
  • 相关阅读:
    StructureMap经典的IoC/DI容器
    移植的7zip到Vxworks 取名vx7zip
    试验Boost在Vxworks上的应用日记 三
    Log4cpp 崩溃
    试验Boost在Vxworks上的应用日记 一
    Vx7zip改进
    GoAhead 2.5 Web Server 网页ROM化的改进
    试验Boost在Vxworks上的应用日记 二
    原来CoreBluetooth 只支持Bluetooth Low Energy
    可变长结构体
  • 原文地址:https://www.cnblogs.com/zouhong/p/16398806.html
Copyright © 2020-2023  润新知