zoukankan      html  css  js  c++  java
  • Spring Boot (32) Lock 本地锁

    平时开发中,有时会双击提交表单造成重复提交,或者网速比较慢时还没有响应又点击了按钮,我们在开发中必须防止重复提交

    一般在前台进行处理,定义个变量,发送请求前判断变量值为true,然后把变量设置为false,可以防止重复提交问题。如果前台没有做这个控制那就需要后端来处理

    Lock 注解

      创建一个LocalLock注解,简单一个key就行了,由于暂时还未用到redis所以expire是摆设

    import java.lang.annotation.*;
    
    //作用于方法
    @Target(ElementType.METHOD)
    //整个运行阶段都可见
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface LocalLock {
        String key() default "";
    
        /**
         * 过期时间 由于用的guava 暂时就忽略这个属性 集成redis时需要用到
         */
        int expire() default 5;
    }

    Lock拦截器(AOP)

      首先通过CacheBuilder.newBuilder()构建出缓存对象,然后设置好过期时间,目的是为了防止因程序崩溃得不到释放。

    在uti的interceptor()方法上采用的十Around(环绕增强),所有带LocalLock注解的都将被切面处理,如果想更为灵活,key的生成规则可以定义成接口形式。

    package com.spring.boot.utils;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.context.annotation.Configuration;
    import com.google.common.cache.Cache;
    import com.google.common.cache.CacheBuilder;
    
    import java.lang.reflect.Method;
    import java.util.concurrent.TimeUnit;
    
    @Aspect
    @Configuration
    public class LockMethodInterceptor {
        //本地缓存
        private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
                //最大1000个
                .maximumSize(1000)
                //设置写缓存后5秒钟过期
                .expireAfterAccess(5, TimeUnit.SECONDS)
                .build();
    
        @Around("execution(public * *(..)) && @annotation(com.spring.boot.utils.LocalLock)")
        public Object interceptor(ProceedingJoinPoint pjp) {
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            LocalLock localLock = method.getAnnotation(LocalLock.class);
            String key = getKey(localLock.key(), pjp.getArgs());
            if (key != null || key != "") {
                if (CACHES.getIfPresent(key) != null) {
                    throw new RuntimeException("请勿重复请求");
                }
                // 如果是第一次请求,就将 key 当前对象压入缓存中
                CACHES.put(key, key);
            }
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                throw new RuntimeException("服务器异常");
            } finally {
                //CACHES.invalidate(key); 完成后移除key
            }
        }
    
        private String getKey(String key, Object[] args) {
            for (int i = 0; i < args.length; i++) {
                key = key.replace("arg[" + i + "]", args[i].toString());
            }
            return key;
        }
    }

    控制层

      在接口上添加@LocalLock(key = "book:arg[0]"); 意味着会将arg[0]替换成第一个参数的值,生成后的新key将被缓存起来;

    package com.spring.boot.controller;
    
    import com.spring.boot.utils.LocalLock;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/books")
    public class BookController {
        @LocalLock(key="book:arg[0]")
        @GetMapping
        public String query(@RequestParam String token){
            return "success - " + token;
        }
    }

    启动项目测试

    第一次请求

    第二次请求

  • 相关阅读:
    怎么与用户有效的沟通以获取用户的真实需求?
    面向过程(或者叫结构化)分析方法与面向对象分析方法到底区别在哪里?
    当下大部分互联网创业公司为什么都愿意采用增量模型来做开发?
    【第八周】回到起点,从头再来
    【第七周作业】项目开发心得
    【第六周作业】项目代码的编写规范
    【第五周作业】寸步难行
    【第四周作业】参加项目开发之后的一些体会
    【第三周作业】对于软件工程学的一些理解
    【第二周作业】面向过程(或者叫结构化)分析方法与面向对象分析方法到底区别在哪里?
  • 原文地址:https://www.cnblogs.com/baidawei/p/9188055.html
Copyright © 2011-2022 走看看