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

下面我们利用自定义注解Spring AopGuava 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本地锁实践的更多相关文章

  1. SpringBoot2.X最佳实践《一》 之 SpringBoot2.x初体验

    SpringBoot2.X最佳实践 前言 本系列文章,从零基础接触  SpringBoot2.x新版本,基础入门使用,热部署,到整合各个主流框架Redis4.x,消息队列AciveMQ, Rocket ...

  2. SpringBoot2 参数管理实践,入参出参与校验

    一.参数管理 在编程系统中,为了能写出良好的代码,会根据是各种设计模式.原则.约束等去规范代码,从而提高代码的可读性.复用性.可修改,实际上个人觉得,如果写出的代码很好,即别人修改也无法破坏原作者的思 ...

  3. Springboot分布式锁实践(redis)

    springboot2本地锁实践一文中提到用Guava Cache实现锁机制,但在集群中就行不通了,所以我们还一般要借助类似Redis.ZooKeeper 之类的中间件实现分布式锁,下面我们将利用自定 ...

  4. spring-boot-2.0.3之quartz集成,最佳实践

    前言 开心一刻 快过年了,大街上,爷爷在给孙子示范摔炮怎么放,嘴里还不停念叨:要像这样,用劲甩才能响.示范了一个,两个,三个... 孙子终于忍不住了,抱着爷爷的腿哭起来:爷呀,你给我剩个吧! 新的一年 ...

  5. Springboot 2.x 如何解决重复提交 (本地锁的实践)

    有没有遇到过这种情况:网页响应很慢,提交一次表单后发现没反应,然后你就疯狂点击提交按钮(12306就经常被这样怒怼),如果做过防重复提交还好,否则那是什么级别的灾难就不好说了... 本文主要是应用 自 ...

  6. spring-boot-2.0.3之quartz集成,数据源问题,源码探究

    前言 开心一刻 着火了,他报警说:119吗,我家发生火灾了. 119问:在哪里? 他说:在我家. 119问:具体点. 他说:在我家的厨房里. 119问:我说你现在的位置. 他说:我趴在桌子底下. 11 ...

  7. 转: springboot2.0下hystrix.stream 404

    springboot2.0下hystrix dashboard Unable to connect to Command Metric Stream解决办法https://blog.csdn.net/ ...

  8. (转)调优 DB2 UDB v8.1 及其数据库的最佳实践

    原文:https://www.ibm.com/developerworks/cn/data/library/techarticles/dm-0404mcarthur/index.html 简介 性能是 ...

  9. SpringBoot2.x 整合Spring-Session实现Session共享

    SpringBoot2.x 整合Spring-Session实现Session共享 1.前言 发展至今,已经很少还存在单服务的应用架构,不说都使用分布式架构部署, 至少也是多点高可用服务.在多个服务器 ...

随机推荐

  1. vue v-for 渲染完成回调

    vue开发中等待vue渲染完成后加载数据的回调方法 Vue.nextTick(function () { alert("加载完成!"); }) 特别注意,如果使用ajax异步渲染的 ...

  2. Pixhawk---基于NSH的Firmware开发与调试

    1 相关知识了解 1.1 Nuttx系统   嵌入式实时操作系统(RTOS). 强调标准兼容和小型封装,具有从8位到32位微控制器环境的高度可扩展性.NuttX 主要遵循 Posix 和 ANSI 标 ...

  3. Android - 加入Android的OpenCV依赖库(Android Dependencies) 问题

    加入Android的OpenCV依赖库(Android Dependencies) 问题 本文地址: http://blog.csdn.net/caroline_wendy 假设想要加入OpenCV的 ...

  4. poj 2528 Mayor&#39;s posters 【线段树 + 离散化】

    Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 50643   Accepted: 14675 ...

  5. Combo Select – jQuery可搜索下拉框插件

    今天用到另一款jq插件..插件就是无脑,,引入相关文件,开始撸吧 引入相关文件: <link href="~/Scripts/combo-select/combo.select.css ...

  6. Five Invaluable Techniques to Improve Regex Performance

    Regular expressions are powerful, but with great power comes great responsibility. Because of the wa ...

  7. Android截图<包括Alertdiaog>

    1.使用的系统内部的截屏方法…… 2. public class MainActivity extends AppCompatActivity { private static final Strin ...

  8. Java 自带MD5 校验文件

    http://www.iteye.com/topic/1127319 前天第一次发表博客到论坛,关于Java文件监控一文,帖子地址在:http://www.iteye.com/topic/112728 ...

  9. where条件顺序与建索引顺序

    查询时,如果数据量很大,where 后面的条件与建索引的顺序相同,也没有什么多少差别,聚集索引稍微快点; 但where 后面的条件与建索引顺序不同,速度会慢下来,到底慢多少,不同的机器会不一样,没有绝 ...

  10. ZBrush中平滑笔刷介绍

    平滑笔刷在ZBrush®中的使用颇多,它可以在ZBrush®模型的多层细分下工作,并且能够控制对模型的平滑效果,而且还能将模型的细节完整保留.默认情况下,按住Shift键就会切换到平滑笔刷,根据调整不 ...