假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许因为网速的原因,没有跳转到新的页面,这时的用户会再次点击提交按钮,如果没有经过处理的话,这时用户就会生成两份订单,类似于这种场景都叫重复提交。
使用redis的setnx和getset命令解决表单重复提交的问题。
1.引入redis依赖和aop依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-redis</artifactId>
- <version>1.3.8.RELEASE</version>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
2.编写加锁和解锁的方法。
- /**
- * @author wangbin
- * @description redis分布式锁
- * @date 2019年09月20日
- */
- @Component
- public class RedisLock {
-
- private final Logger logger = LoggerFactory.getLogger(RedisLock.class);
-
- @Autowired
- private StringRedisTemplate redisTemplate;
-
- /**
- * @author wangbin
- * @description 进行加锁的操作(该方法是单线程运行的)
- * @date 2019年09月20日
- * @param key 某个方法请求url加上cookie中的用户身份使用md5加密生成
- * @param value 当前时间+过期时间(10秒)
- * @return true表示加锁成功 false表示未获取到锁
- */
- public boolean lock(String key,String value){
- //加锁成功返回true
- if(redisTemplate.opsForValue().setIfAbsent(key,value,10, TimeUnit.SECONDS)){
- return true;
- }
- String currentValue = redisTemplate.opsForValue().get(key);
- //加锁失败,再判断是否由于解锁失败造成了死锁的情况
- if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){
- //获取上一个锁的时间,并且重新设置锁
- String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
- if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)){
- //设置成功,重新设置锁是保证了单线程的运行
- return true;
- }
- }
- return false;
- }
-
- /**
- * @author wangbin
- * @description 进行解锁的操作
- * @date 2019年09月20日
- * @param key 某个方法请求url使用md5加密生成
- * @param value 当前时间+过期时间
- * @return
- */
- public void unLock(String key,String value){
- try {
- String currentValue = redisTemplate.opsForValue().get(key);
- if(StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)){
- redisTemplate.delete(key);
- }
- }catch (Exception e){
- logger.error("redis分布式锁,解锁异常",e);
- }
- }
-
-
- /**
- * @author wangbin
- * @description 进行解锁的操作
- * @date 2019年09月20日
- * @param key 某个方法请求url使用md5加密生成
- * @return
- */
- public void unLock(String key){
- try {
- String currentValue = redisTemplate.opsForValue().get(key);
- if(StringUtils.isNotEmpty(currentValue)){
- redisTemplate.delete(key);
- }
- }catch (Exception e){
- logger.error("redis分布式锁,解锁异常",e);
- }
- }
- }
3.使用拦截器在请求之前进行加锁的判断。
- @Configuration
- public class LoginInterceptor extends HandlerInterceptorAdapter {
- private final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
- //超时时间设置为10秒
- private static final int timeOut = 10000;
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
- @Autowired
- private RedisLock redisLock;
- /**
- * 在请求处理之前进行调用(Controller方法调用之前)
- * 基于URL实现的拦截器
- * @param request
- * @param response
- * @param handler
- * @return
- * @throws Exception
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- String path = request.getServletPath();
- if (path.matches(Constants.NO_INTERCEPTOR_PATH)) {
- //不需要的拦截直接过
- return true;
- } else {
- // 这写你拦截需要干的事儿,比如取缓存,SESSION,权限判断等
- //判断是否是重复提交的请求
- if(!redisLock.lock(DigestUtils.md5Hex(request.getRequestURI()+value),String.valueOf(System.currentTimeMillis()+timeOut))){
- logger.info("===========获取锁失败,该请求为重复提交请求");
- return false;
- }
- return true;
- }
- }
- }
4.使用aop在后置通知中进行解锁。
- /**
- * @author wangbin
- * @description 使用redis分布式锁解决表单重复提交的问题
- * @date 2019年09月20日
- */
- @Aspect
- @Component
- public class RepeatedSubmit {
-
- @Autowired
- private RedisLock redisLock;
-
- //定义切点
- @Pointcut("execution(public * com.kunluntop.logistics.controller..*.*(..))")
- public void pointcut(){
-
- }
-
- //在方法执行完成后释放锁
- @After("pointcut()")
- public void after(){
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- HttpServletRequest request = attributes.getRequest();
- redisLock.unLock(DigestUtils.md5Hex(request.getRequestURI()+ CookieUtils.getCookie(request,"userkey")));
- }
- }
到此这篇关于redis分布式锁解决表单重复提交的问题的文章就介绍到这了,更多相关redis 表单重复提交内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!