java 后端做重复提交拦截基于aop

基于注解配置与aop完美结合。在指定时间内。用redis+lua脚本获取锁。不会出现插队。看代码实现。

1、创建AopInterceptor

/**
 * @author:wwz
 */
@Aspect
@Component
public class AopInterceptor{
    @Autowired
    private RedisUtils redisUtils;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    @Pointcut("@annotation(com.ditop.modules.gcjsy.company.wsba.constant.RejectMultiPost)")
    public void pointcut(){
    }

    private final  static class Holder{
        private static HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }
    @Around("pointcut()&& @annotation(rejectMultiPost)")
    public Object around(ProceedingJoinPoint joinPoint, RejectMultiPost rejectMultiPost){
        String key = null;
        try {
            key = PermissionHelper.currentUserSession().getUserId()+Holder.request.getRequestURI();
            //这里是redis+lua。一个key在指定时间内。只获取一次。不会出现插队
            if(redisUtils.lock(key,key,rejectMultiPost.timeout())){
                return joinPoint.proceed(joinPoint.getArgs());
            }else{
                logger.error("重复提交【代码未执行】");
                System.out.println("重复提交【代码未执行】");
            }
        } catch (Throwable throwable) {
            logger.error(throwable.getMessage());
            throwable.printStackTrace();
            if(key!=null){
                redisUtils.unLock(key,key);
            }else{
                try {
                    return joinPoint.proceed(joinPoint.getArgs());
                } catch (Throwable throwable1) {
                    logger.error(throwable1.getMessage());
                    throwable1.printStackTrace();
                }
            }

        }
       return JsonResult.success();
    }
}

 

2、创建RedisUtils

/**
 * @author wwz
 * @date 2020-05-15
 * @descrption:
 */
@Component
public class RedisUtils {

    private final  Logger log = LoggerFactory.getLogger(this.getClass());
   /* private final  String expireTime = "5000";*/

    @SuppressWarnings("rawtypes")
    @Autowired
    private RedisTemplate stringRedisTemplateDemo;

    private DefaultRedisScript getLockRedisScript;
    private DefaultRedisScript releaseLockRedisScript;

    private StringRedisSerializer argsStringSerializer = new StringRedisSerializer();
    private StringRedisSerializer resultStringSerializer = new StringRedisSerializer();

    private final String EXEC_RESULT = "1";
    @SuppressWarnings("unchecked")
    @PostConstruct
    private void init() {
        getLockRedisScript = new DefaultRedisScript();
        getLockRedisScript.setResultType(String.class);
        releaseLockRedisScript = new DefaultRedisScript();
        releaseLockRedisScript.setResultType(String.class);

        // 初始化装载 lua 脚本
        getLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/lock.lua")));
        releaseLockRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua")));

    }

    /**
     * 加锁操作
     * @param key Redis 锁的 key 值
     * @param requestId 请求id,防止解了不该由自己解的锁 (随机生成)
     * @param expireTime 锁的超时时间(毫秒)
     * @param retryTimes 获取锁的重试次数
     * @return true or false
     */

    @SuppressWarnings("unchecked")
    public boolean lock(String key, String requestId, String expireTime) {
            int retryTimes = 1;

        try {
            int count = 0;
            while (true) {
                Object result = stringRedisTemplateDemo.execute(getLockRedisScript, argsStringSerializer, resultStringSerializer,
                        Collections.singletonList(key), requestId, expireTime);
                log.debug("result:{},type:{}", result, result.getClass().getName());
                if (EXEC_RESULT.equals(result)) {
                    return true;
                } else {
                    count++;
                    if (retryTimes == count) {
                        log.warn("has tried {} times , failed to acquire lock for key:{},requestId:{}", count, key, requestId);
                        return false;
                    } else {
                        log.warn("try to acquire lock {} times for key:{},requestId:{}", count, key, requestId);
                        Thread.sleep(500);
                        continue;
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解锁操作
     * @param key Redis 锁的 key 值
     * @param requestId 请求 id, 防止解了不该由自己解的锁 (随机生成)
     * @return true or false
     */
    @SuppressWarnings("unchecked")
    public boolean unLock(String key, String requestId) {
        Object result = stringRedisTemplateDemo.execute(releaseLockRedisScript, argsStringSerializer, resultStringSerializer,
                Collections.singletonList(key), requestId);
        if (EXEC_RESULT.equals(result)) {
            return true;
        }
        return false;
    }

}

3、引入lua脚本。放到resource目录下

lock.lua:

if redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2]) then
    return '1'
else
    return '0'
end

 

releaseLock.lua:

if redis.call('get',KEYS[1]) == ARGV[1] then
    return tostring(redis.call('del',KEYS[1]))
else
    return '0'
end

4、创建注解:RejectMultiPost

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RejectMultiPost {

    /**
     * ms
     * @return
     */
    String timeout() default "5000";

}

5、防止重复操作的方法加入注解

java 后端做重复提交拦截基于aop_第1张图片

 

超时时间可以自己配置。这个拦截的思路是。某个用户在指定时间内不能请求相同地址。完美破解同一个用户在不同浏览器同时操作一条数据以及同一个浏览器重复单击按钮。不足之处请指出。同是学习

同一个浏览器

你可能感兴趣的:(redis,java,spring,java,aop,redis)