Spring Boot如何防止重复提交
作者:谈胖胖 发布时间:2021-11-10 16:23:42
标签:Spring,Boot,重复提交
场景:同一个用户在2秒内对同一URL的提交视为重复提交。
思考逻辑:
1.从数据库方面考虑,数据设计的时候,某些数据有没有唯一性,如果有唯一性,要考虑设置唯一索引,可以避免脏数据。
2.从应用层面考虑,首先判断是单机服务还是分布式服务,则此时需要考虑一些缓存,利用缓存,来保证数据的重复提交。
假设是分布式应用,则可以将用户的信息,例如token和请求的url进行组装在一起,存储到缓存存,例如redis,并设置超时时间为2秒,如此来保证数据的唯一性。
以下是代码实现:
Application.java
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author www.spring.tsh
* @功能描述 防重复提交
* @date 2018-08-26
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
application.yml
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
RedisConfig.java
package com.common;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.redis")
public JedisConnectionFactory getConnectionFactory() {
return new JedisConnectionFactory(new RedisStandaloneConfiguration(), JedisClientConfiguration.builder().build());
}
@Bean
<K, V> RedisTemplate<K, V> getRedisTemplate() {
RedisTemplate<K, V> redisTemplate = new RedisTemplate<K, V>();
redisTemplate.setConnectionFactory(getConnectionFactory());
return redisTemplate;
}
}
自定义注解NoRepeatSubmit.java
package com.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 作用到方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
/**
* @功能描述 防止重复提交标记注解
* @author www.srping.tsh
* @date 2018-08-26
*/
public @interface NoRepeatSubmit {
}
aop解析注解NoRepeatSubmitAop.java
package com.common;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
/**
* @功能描述 aop解析注解
* @author www.gaozz.club
* @date 2018-11-02
*/
public class NoRepeatSubmitAop {
private Log logger = LogFactory.getLog(getClass());
@Autowired
private RedisTemplate<String, Integer> template;
@Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
ValueOperations<String, Integer> opsForValue = template.opsForValue();
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
HttpServletRequest request = attributes.getRequest();
String key = sessionId + "-" + request.getServletPath();
if (opsForValue.get(key) == null) {// 如果缓存中有这个url视为重复提交
Object o = pjp.proceed();
opsForValue.set(key, 0, 2, TimeUnit.SECONDS);
return o;
} else {
logger.error("重复提交");
return null;
}
} catch (Throwable e) {
e.printStackTrace();
logger.error("验证重复提交时出现未知异常!");
return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
}
}
}
测试类:
package com.example;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.common.NoRepeatSubmit;
/**
* @功能描述 测试Controller
* @author www.spring.tsh
* @date 2018-08-26
*/
@RestController
public class TestController {
@RequestMapping("/test")
@NoRepeatSubmit
public String test() {
return ("程序逻辑返回");
}
}
来源:https://blog.csdn.net/zhuyanlin09/article/details/91626111


猜你喜欢
- 本文实例讲述了C#串口通信实现方法。分享给大家供大家参考。具体方法如下:通过COM1发送数据,COM2接收数据。当COM2接收完本次发送的数
- 问题描述:图片加载后显示,然后进行删除操作时提示“……正由另一进程使用,因此该进程无法访问该文件。……”解决办法:原代码:iml.Image
- 前言:项目中经常会用到类似于QQ侧滑点击删除的效果,网上的开源库也很多。个人感觉SwipeLayout最好用。下面介绍怎么使用。一、首先导入
- 一、html代码 &n
- 之前由于项目需要比较详细地学习了Spring Security的相关知识,并打算实现一个较为通用的权限管理模块。由于项目是前后端分离的,所以
- spring注解配置实现事务控制1、导入相关依赖这个项目当中使用了spring的JdbcTemplate模板类来用在控制层简化jdbc代码,
- 前言在实际工作中,重处理是一个非常常见的场景,比如:发送消息失败。调用远程服务失败。争抢锁失败。这些错误可能是因为网络波动造成的,等待过后重
- 简介switch的新特性可是源远流长,早在JDK 12就以预览功能被引入了,最终在JDK 14成为了正式版本的功能:JEP 361: Swi
- 一、 简介Opitonal是java8引入的一个新类,目的是为了解决空指针异常问题。本质上,这是一个包含有可选值的包装类,这意味着 Opti
- 一、布局文件part.xml:<RelativeLayout xmlns:android="http://schemas.a
- RestTemplate 请求接收自定义400+ 或500+错误场景当服务端自定义400错误返回体时,使用restTemplate 请求接收
- 本文实例讲述了C#动态创建button的方法。分享给大家供大家参考。具体实现方法如下:using System;using System.C
- 熟悉Android的朋友们都知道,不管是微博客户端还是新闻客户端,都离不开列表组件,可以说列表组件是Android数据展现方面最重要的组件,
- 一、Servlet3.0异步请求@WebServlet(value = "/async", asyncSupported
- Chart控件可以用来绘制波形图、柱状图、饼图、折线图等,用来进行数据表现是很不错的简单说一下这个控件的使用方法效果图我们首先要加载Char
- 一、安装及配置Genymotion(1)由于Eclipse中自带的SDK模拟器,启动之慢,不说了 现在给大家介绍一种比较快的模拟器Genym
- 本文实例讲述了C#中datatable序列化与反序列化,分享给大家供大家参考。具体方法如下:一、datatable序列化public str
- 简单的IM聊天程序由于项目需要做一个基于XMPP协议的Android通讯软件。故开始研究XMPP。XMPP协议采用的是客户端-服务器架构,所
- 一,栈1,概念在我们软件应用 ,栈这种后进先出数据结构的应用是非常普遍的。比如你用浏 览器上网时不管什么浏览器都有 个"后退&qu
- Lambda是第十一个希腊字母,大写Λ,小写λ,额,跑题了…Lambda表达式 是Java8的新特性之一:Lambda表达式函数式接口流AP