zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Springboot2本地锁实践详解编程语言

编程语言 详解 实践 本地 SpringBoot2
2023-06-13 09:11:47 时间

在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交….

下面我们利用自定义注解、Spring Aop、Guava Cache 实现表单防重复提交

一、导入依赖

 创建springboot项目,在pom.xml文件中加入以下内容

 dependencies 

 dependency 

 groupId org.springframework.boot /groupId 

 artifactId spring-boot-starter-web /artifactId 

 /dependency 

 dependency 

 groupId org.springframework.boot /groupId 

 artifactId spring-boot-starter-aop /artifactId 

 /dependency 

 dependency 

 groupId com.google.guava /groupId 

 artifactId guava /artifactId 

 version 21.0 /version 

 /dependency 

 /dependencies 
 二、Lock注解

创建一个 LocalLock 注解,就一个 key 可以了

 package com.carry.annotation; 

 import java.lang.annotation.Documented; 

 import java.lang.annotation.ElementType; 

 import java.lang.annotation.Inherited; 

 import java.lang.annotation.Retention; 

 import java.lang.annotation.RetentionPolicy; 

 import java.lang.annotation.Target; 

 /** 

 * 锁的注解 

 @Target(ElementType.METHOD) 

 @Retention(RetentionPolicy.RUNTIME) 

 @Documented 

 @Inherited 

 public @interface LocalLock { 

 String key() default ""; 

 }
 三、Lock拦截器(AOP)

首先通过 CacheBuilder.newBuilder() 构建出缓存对象,设置好过期时间;其目的就是为了防止因程序崩溃锁得不到释放,然后在具体的 interceptor() 方法上采用的是 Around(环绕增强) ,所有带 LocalLock 注解的都将被切面处理

具体代码

 package com.carry.interceptor; 

 import java.lang.reflect.Method; 

 import java.util.concurrent.TimeUnit; 

 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 org.springframework.util.StringUtils; 

 import com.carry.annotation.LocalLock; 

 import com.google.common.cache.Cache; 

 import com.google.common.cache.CacheBuilder; 

 @Aspect 

 @Configuration 

 public class LockMethodInterceptor { 

 private static final Cache String, Object CACHES = CacheBuilder.newBuilder() 

 // 最大缓存 100 个 

 .maximumSize(100) 

 // 设置写缓存后 5 秒钟过期 

 .expireAfterWrite(5, TimeUnit.SECONDS).build(); 

 @Around("execution(public * *(..)) @annotation(com.carry.annotation.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 (!StringUtils.isEmpty(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 { 

 // TODO 

 /** 

 * key 的生成策略,如果想灵活可以写成接口与实现类的方式 

 * @param keyExpress 

 * 表达式 

 * @param args 

 * 参数 

 * @return 生成的key 

 private String getKey(String keyExpress, Object[] args) { 

 for (int i = 0; i args.length; i++) { 

 keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString()); 

 return keyExpress; 

 }
四、控制层

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

具体代码

 package com.carry.controller; 

 import org.springframework.web.bind.annotation.GetMapping; 

 import org.springframework.web.bind.annotation.RequestMapping; 

 import org.springframework.web.bind.annotation.RequestParam; 

 import org.springframework.web.bind.annotation.RestController; 

 import com.carry.annotation.LocalLock; 

 @RestController 

 @RequestMapping("/test") 

 public class LocalLockController { 

 @LocalLock(key = "key:arg[0]") 

 @GetMapping 

 public String query(@RequestParam String token) { 

 return "success - " + token; 

 }

启动项目,在postman中输入url:localhost:8080/test?token=1

第一次请求结果:

Springboot2本地锁实践详解编程语言

第二次请求结果:

Springboot2本地锁实践详解编程语言

 

17000.html

cgojavaxml