一、防重複提交 AOP
在項目中,我們經常會遇到用戶不小心重複提交的情況,既會造成服務器負載過高,又會影響用戶體驗,因此我們需要對此進行防範處理。其中一種方法是使用AOP面向切面編程的方式解決。具體步驟如下:
1、首先,我們需要定義一個註解,用於標記需要進行防重複提交處理的方法或類,如下所示:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicateSubmit {
}
2、然後,定義一個切面來攔截被標記的方法或類,並在攔截的過程中對重複提交進行處理,如下所示:
@Component
@Aspect
public class PreventDuplicateSubmitAspect {
@Autowired
private Cache cache;
@Around("@annotation(com.example.demo.annotation.PreventDuplicateSubmit)")
public Object preventDuplicateSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = attributes.getSessionId();
String methodName = joinPoint.getSignature().toLongString();
if (cache.getIfPresent(sessionId + methodName) == null) {
cache.put(sessionId + methodName, 1);
Object result = joinPoint.proceed();
return result;
} else {
throw new RuntimeException("請勿重複提交");
}
}
}
3、使用定義好的註解標記需要進行防重複提交處理的方法或類,如下所示:
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/submit")
@PreventDuplicateSubmit
public String submit(@RequestBody Map params) {
// ...
}
// ...
}
通過以上步驟,我們就可以使用AOP來防止重複提交了。
二、防止請求重複提交
在使用AOP進行防重複提交時,我們並沒有考慮到兩個不同的請求可能會同時進入同一個方法,從而引發重複提交。因此,我們需要在方法內部進行處理,以避免這種情況的出現。具體步驟如下:
1、在方法內部添加一個鎖,如下所示:
@PostMapping("/submit")
public String submit(@RequestBody Map params) {
synchronized (this) {
// ...
}
}
2、使用一個Set集合來保存已經提交過的參數,如下所示:
private Set submittedParams = new HashSet();
@PostMapping("/submit")
public String submit(@RequestBody Map params) {
if (!submittedParams.add(params.toString())) {
throw new RuntimeException("請勿重複提交");
}
// ...
}
通過以上兩種方式,我們可以有效地防止請求重複提交。
三、防重複提交註解
在使用AOP的方式進行防重複提交時,我們需要手動在每個需要進行防重複提交處理的方法或類上添加一個註解。為了方便使用,我們可以自定義一個註解,並通過註解方式來實現防重複提交。具體步驟如下:
1、定義一個註解,如下所示:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicateSubmit {
}
2、定義一個切面,並在切面上使用該註解,如下所示:
@Component
@Aspect
public class PreventDuplicateSubmitAspect {
@Autowired
private Cache cache;
@Around("@annotation(com.example.demo.annotation.PreventDuplicateSubmit)")
public Object preventDuplicateSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = attributes.getSessionId();
String methodName = joinPoint.getSignature().toLongString();
if (cache.getIfPresent(sessionId + methodName) == null) {
cache.put(sessionId + methodName, 1);
Object result = joinPoint.proceed();
return result;
} else {
throw new RuntimeException("請勿重複提交");
}
}
}
3、使用自定義的註解標記需要進行防重複提交處理的方法或類,如下所示:
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/submit")
@PreventDuplicateSubmit
public String submit(@RequestBody Map params) {
// ...
}
// ...
}
通過以上步驟,我們可以使用自定義註解的方式來防止重複提交。
四、防重複提交 AOP Express
Spring AOP的使用有時候太過繁瑣,因此我們可以使用AOP Express來簡化操作。具體步驟如下:
1、在pom.xml文件中引入AOP Express相關依賴包,如下所示:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>com.googlecode.easyspring</groupId>
<artifactId>easy-aop-express</artifactId>
<version>2.2.0</version>
</dependency>
2、在方法或類上添加一個AOP Express的註解,如下所示:
@MethodAround("execution(* com.example.demo.controller.TestController.submit(..))")
@PreventDuplicateSubmit
public Object preventDuplicateSubmit(JoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String sessionId = attributes.getSessionId();
String methodName = joinPoint.getSignature().toLongString();
if (cache.getIfPresent(sessionId + methodName) == null) {
cache.put(sessionId + methodName, 1);
Object result = joinPoint.proceed();
return result;
} else {
throw new RuntimeException("請勿重複提交");
}
}
通過以上步驟,我們可以使用AOP Express來進行防重複提交處理。
五、防重複提交的最佳方案
不同的場景下,我們需要採用不同的方式進行防重複提交處理。對於高並發、分布式的應用程序,我們需要使用一種能夠支持分布式鎖的最佳方案來保證數據的一致性和安全性。具體步驟如下:
1、配置Redis分布式鎖相關信息,如下所示:
spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.database=0
2、定義一個分布式鎖的工具類,如下所示:
@Component
public class RedisDistributedLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 獲取鎖
* @param key 鎖的名字
* @param value 鎖的值
* @param expireTime 鎖的過期時間
* @return 是否獲取鎖成功
*/
public boolean tryLock(String key, String value, int expireTime) {
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
String result = connection.execute("SET", key.getBytes(), value.getBytes(), "NX".getBytes(), "EX".getBytes(), String.valueOf(expireTime).getBytes());
connection.close();
return result != null && result.equalsIgnoreCase("OK");
}
/**
* 釋放鎖
* @param key 鎖的名字
* @param value 鎖的值
*/
public void unlock(String key, String value) {
List keys = new ArrayList();
keys.add(key);
List values = new ArrayList();
values.add(value);
Long result = redisTemplate.execute(new DefaultRedisScript<>(releaseLockScript, Long.class), keys, values);
}
}
3、在方法內部使用分布式鎖進行防重複提交處理,如下所示:
@PostMapping("/submit")
public String submit(@RequestBody Map params) {
String key = "submit_lock";
String value = UUID.randomUUID().toString();
int expireTime = 60;
if (!redisDistributedLock.tryLock(key, value, expireTime)) {
throw new RuntimeException("請勿重複提交");
}
try {
// ...
} finally {
redisDistributedLock.unlock(key, value);
}
}
通過以上步驟,我們就可以在高並發、分布式的環境下使用分布式鎖來實現防重複提交的最佳方案。
六、防重複提交解決方案
以上方案在不同的場景下都可以有效地解決防重複提交的問題。在實際應用中,我們需要根據具體項目需求來選擇適合自己的方案。同時,我們可以將上述各種方案進行組合使用,以達到更加全面、完善的防重複提交解決方案。
七、防重複提交分布式鎖最好嗎
防重複提交分布式鎖是目前最好的一種解決方案,它不僅可以防止重複提交,還可以保證數據的一致性和安全性。但是,在實際應用中,我們還需要考慮到分布式鎖的性能、可用性、並發性等方面的問題。因此,在使用分布式鎖時,我們需要注意分布式鎖算法的選擇、鎖的粒度、鎖的超時時間等問題,以便在最大程度上提升系統的性能和可用性。
八、SpringBoot防重複提交
SpringBoot框架為我們提供了很多便捷的方式來實現防重複提交。在本文中,我們已經使用了很多SpringBoot的功能,如AOP、緩存、Redis等。因此,在使用SpringBoot進行開發時,我們可以直接使用這些功能來實現防重複提交處理,而不需要額外引入其他第三方框架。
九、前端防止重複提交
在前端,我們可以採用禁止重複提交按鈕、延時提交、加載提示等方式來防止重複提交,具體實現方式如下:
1、禁止重複提交按鈕
在提交按鈕觸發以後,將按鈕設置為不可用狀態,在提交請求響應以後將按鈕重新設置為可用狀態,從而避免用戶重複點擊提交按鈕。
$("#submitBtn").click(function() {
$(this).attr("disabled", true);
$.ajax({
type: "POST",
url: "/submit",
data: param,
success: function() {
$("#submitBtn").attr("disabled", false);
// ...
},
error: function() {
$("#submitBtn").attr("disabled", false);
// ...
}
});
});
2、延時提交
在提交按鈕觸發以後,延遲一定時間再進行提交,這樣即使用戶多次點擊提交按鈕,也只會進行一次提交操作。
$("#submitBtn").click(function() {
$(this).attr("disabled", true);
setTimeout(function() {
$.ajax({
type: "POST",
url: "/submit",
data: param,
success: function() {
$("#submitBtn").attr("disabled", false);
// ...
},
error: function() {
$("#submitBtn").attr("disabled", false);
// ...
}
});
}, 1000);
});
3、加載提示
在提交請求響應以前,顯示一個加載提示框,從而讓用戶知道他們已經提交了數據,並且需要等待服務器的響應。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/292088.html
微信掃一掃
支付寶掃一掃