spring boot 防止重復(fù)提交實(shí)現(xiàn)方法詳解
本文實(shí)例講述了spring boot 防止重復(fù)提交實(shí)現(xiàn)方法。分享給大家供大家參考,具體如下:
服務(wù)器端實(shí)現(xiàn)方案:同一客戶端在2秒內(nèi)對同一URL的提交視為重復(fù)提交
上代碼吧
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>springboot-repeat-submit</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<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>24.0-jre</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application.java
package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author www.gaozz.club
* @功能描述 防重復(fù)提交
* @date 2018-08-26
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
自定義注解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) // 運(yùn)行時有效
/**
* @功能描述 防止重復(fù)提交標(biāo)記注解
* @author www.gaozz.club
* @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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.google.common.cache.Cache;
@Aspect
@Component
/**
* @功能描述 aop解析注解
* @author www.gaozz.club
* @date 2018-08-26
*/
public class NoRepeatSubmitAop {
private Log logger = LogFactory.getLog(getClass());
@Autowired
private Cache<String, Integer> cache;
@Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
HttpServletRequest request = attributes.getRequest();
String key = sessionId + "-" + request.getServletPath();
if (cache.getIfPresent(key) == null) {// 如果緩存中有這個url視為重復(fù)提交
Object o = pjp.proceed();
cache.put(key, 0);
return o;
} else {
logger.error("重復(fù)提交");
return null;
}
} catch (Throwable e) {
e.printStackTrace();
logger.error("驗(yàn)證重復(fù)提交時出現(xiàn)未知異常!");
return "{\"code\":-889,\"message\":\"驗(yàn)證重復(fù)提交時出現(xiàn)未知異常!\"}";
}
}
}
緩存類
package com.common;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
@Configuration
/**
* @功能描述 內(nèi)存緩存
* @author www.gaozz.club
* @date 2018-08-26
*/
public class UrlCache {
@Bean
public Cache<String, Integer> getCache() {
return CacheBuilder.newBuilder().expireAfterWrite(2L, TimeUnit.SECONDS).build();// 緩存有效期為2秒
}
}
測試Controller
package com.example;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.common.NoRepeatSubmit;
/**
* @功能描述 測試Controller
* @author www.gaozz.club
* @date 2018-08-26
*/
@RestController
public class TestController {
@RequestMapping("/test")
@NoRepeatSubmit
public String test() {
return ("程序邏輯返回");
}
}
瀏覽器輸入http://localhost:8080/test
然后F5刷新查看效果

以下為新版內(nèi)容:解決了程序集群部署時請求可能會落到多臺機(jī)器上的問題,把內(nèi)存緩存換成了redis
application.yml
spring: redis: host: 192.168.1.92 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;
}
}
調(diào)整切面類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視為重復(fù)提交
Object o = pjp.proceed();
opsForValue.set(key, 0, 2, TimeUnit.SECONDS);
return o;
} else {
logger.error("重復(fù)提交");
return null;
}
} catch (Throwable e) {
e.printStackTrace();
logger.error("驗(yàn)證重復(fù)提交時出現(xiàn)未知異常!");
return "{\"code\":-889,\"message\":\"驗(yàn)證重復(fù)提交時出現(xiàn)未知異常!\"}";
}
}
}
附:GitHub源碼地址:https://github.com/gzz2017gzz/spring-boot2-example/tree/master/54-spring-boot-repeat-submit-single
更多關(guān)于java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Spring框架入門與進(jìn)階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設(shè)計(jì)有所幫助。
- Spring Boot如何防止重復(fù)提交
- springboot實(shí)現(xiàn)防重復(fù)提交和防重復(fù)點(diǎn)擊的示例
- SpringBoot?+?Redis如何解決重復(fù)提交問題(冪等)
- SpringBoot整合redis+Aop防止重復(fù)提交的實(shí)現(xiàn)
- SpringBoot攔截器實(shí)現(xiàn)項(xiàng)目防止接口重復(fù)提交
- 基于SpringBoot接口+Redis解決用戶重復(fù)提交問題
- SpringBoot+Redis海量重復(fù)提交問題解決
- SpringBoot+Redisson自定義注解一次解決重復(fù)提交問題
相關(guān)文章
StringUtils,CollectionUtils判斷為空的方法和原生代碼哪個效率最高
這篇文章主要介紹了StringUtils,CollectionUtils判斷為空的方法和原生代碼哪個效率最高,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
詳解SpringBoot?統(tǒng)一后端返回格式的方法
今天我們來聊一聊在基于SpringBoot前后端分離開發(fā)模式下,如何友好的返回統(tǒng)一的標(biāo)準(zhǔn)格式以及如何優(yōu)雅的處理全局異常,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2022-05-05
MyBatis-Plus如何關(guān)閉SQL日志打印詳解
在使用mybatisplus進(jìn)行開發(fā)時,日志是一個非常有用的工具,它可以幫助我們更好地了解和調(diào)試我們的代碼,這篇文章主要給大家介紹了關(guān)于MyBatis-Plus如何關(guān)閉SQL日志打印的相關(guān)資料,需要的朋友可以參考下2024-03-03
java使用HttpSession實(shí)現(xiàn)QQ訪問記錄
這篇文章主要介紹了java使用HttpSession實(shí)現(xiàn)QQ的訪問記錄的相關(guān)資料,需要的朋友可以參考下2016-03-03
使用SpringAOP實(shí)現(xiàn)公共字段填充功能
在新增員工或者新增菜品分類時需要設(shè)置創(chuàng)建時間、創(chuàng)建人、修改時間、修改人等字段,在編輯員工或者編輯菜品分類時需要設(shè)置修改時間、修改人等字段,這些字段屬于公共字段,本文將給大家介紹使用SpringAOP實(shí)現(xiàn)公共字段填充功能,需要的朋友可以參考下2024-08-08
一篇文章帶你理解Java Spring三級緩存和循環(huán)依賴
這篇文章主要介紹了淺談Spring 解決循環(huán)依賴必須要三級緩存嗎,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09

