欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java如何防止表單重復提交的注解@RepeatSubmit

 更新時間:2024年11月12日 09:45:11   作者:東方巴黎~Sunsiny  
@RepeatSubmit是一個自定義注解,用于防止表單重復提交,它通過AOP和攔截器模式實現,結合了線程安全和分布式環(huán)境的考慮,注解參數包括interval(間隔時間)和message(提示信息),使用時需要注意并發(fā)處理、用戶體驗、性能和安全性等方面,失效原因是多方面的

代碼解釋

@RepeatSubmit

  • 是一個自定義注解,通常用于防止表單重復提交。
  • 這個注解可以應用于控制器方法上,以確保同一個請求在一定時間內不會被多次提交。

以下是一些常見的參數和用法:

  • value:注解的名稱或描述。
  • interval:兩次請求之間的最小間隔時間(單位通常是毫秒)。
  • message:當檢測到重復提交時返回的提示信息。

示例代碼

假設有一個 @RepeatSubmit 注解的定義如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
    String value() default "";
    int interval() default 3000; // 默認3秒
    String message() default "請勿重復提交";
}

使用示例

在控制器方法中使用 @RepeatSubmit 注解:

@RestController
public class UserController {

    @PostMapping("/submitForm")
    @RepeatSubmit(interval = 5000, message = "請等待5秒后再提交")
    public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
        // 處理表單提交邏輯
        return ResponseEntity.ok("表單提交成功");
    }
}

控制流圖

以下是 @RepeatSubmit 注解的控制流圖,展示了其工作原理:

flowchart TD
A[開始] --> B[接收請求]
B --> C{檢查是否重復提交}
C -->|是| D[返回重復提交提示信息]
C -->|否| E[處理請求]
E --> F[返回成功響應]
F --> G[結束]

說明

  • A: 開始處理請求。
  • B: 接收到客戶端的請求。
  • C: 檢查當前請求是否與前一次請求的時間間隔小于設定的 interval。
  • D: 如果檢測到重復提交,返回提示信息(如 “請等待5秒后再提交”)。
  • E: 如果沒有檢測到重復提交,繼續(xù)處理請求。
  • F:請求處理成功后,返回成功響應。
  • G: 結束請求處理過程。

使用的設計模式

@RepeatSubmit 注解通常結合 AOP(面向切面編程) 和 攔截器模式 來實現防止表單重復提交的功能。

設計模式解析

AOP(面向切面編程):

  • 目的: 將橫切關注點(如日志記錄、事務管理、安全性等)從業(yè)務邏輯中分離出來,提高代碼的模塊化和可維護性。
  • 實現: 使用 Spring AOP 或其他 AOP 框架,通過切面(Aspect)來攔截方法調用,執(zhí)行額外的邏輯(如檢查重復提交)。

攔截器模式

  • 目的: 在請求到達目標方法之前或之后執(zhí)行特定的邏輯。
  • 實現: 在 Spring 中,可以通過 HandlerInterceptor 或 MethodInterceptor 來實現攔截器,攔截請求并執(zhí)行預處理或后處理邏輯

定義注解

   @Target(ElementType.METHOD)
   @Retention(RetentionPolicy.RUNTIME)
   public @interface RepeatSubmit {
       String value() default "";
       int interval() default 3000; // 默認3秒
       String message() default "請勿重復提交";
   }
   

創(chuàng)建切面

   @Aspect
   @Component
   public class RepeatSubmitAspect {

       @Around("@annotation(repeatSubmit)")
       public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
           // 獲取方法簽名
           MethodSignature signature = (MethodSignature) joinPoint.getSignature();
           Method method = signature.getMethod();

           // 獲取請求上下文
           HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

           // 獲取注解參數
           int interval = repeatSubmit.interval();
           String message = repeatSubmit.message();

           // 從 session 中獲取上次請求的時間戳
           Long lastRequestTime = (Long) request.getSession().getAttribute(method.getName());

           // 檢查是否重復提交
           if (lastRequestTime != null && System.currentTimeMillis() - lastRequestTime < interval) {
               throw new RuntimeException(message);
           }

           // 記錄當前請求的時間戳
           request.getSession().setAttribute(method.getName(), System.currentTimeMillis());

           // 繼續(xù)執(zhí)行目標方法
           return joinPoint.proceed();
       }
   }
   

在控制器方法中使用注解

   @RestController
   public class UserController {

       @PostMapping("/submitForm")
       @RepeatSubmit(interval = 5000, message = "請等待5秒后再提交")
       public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
           // 處理表單提交邏輯
           return ResponseEntity.ok("表單提交成功");
       }
   }
   

使用@RepeatSubmit需要注意什么

使用 @RepeatSubmit 注解來防止表單重復提交時,需要注意以下幾個方面:

1. 注解參數配置

  • interval:設置合理的間隔時間。過短的間隔時間可能導致用戶頻繁遇到重復提交的提示,影響用戶體驗;過長的間隔時間可能無法有效防止快速連續(xù)提交。
  • message: 提供明確的提示信息,告知用戶為什么請求被拒絕,幫助用戶理解并采取正確的操作。

2. 并發(fā)處理

  • 線程安全: 在高并發(fā)環(huán)境下,確保時間戳的讀取和寫入操作是線程安全的。
  • 可以使用 ConcurrentHashMap 或 AtomicLong 等線程安全的數據結構來存儲時間戳。
  • 分布式環(huán)境: 如果應用部署在多個服務器上,需要考慮如何在分布式環(huán)境中共享時間戳信息。
  • 可以使用 Redis 等分布式緩存來存儲時間戳。

3. 用戶體驗

  • 前端提示: 在前端頁面上添加防重復提交的機制,如禁用提交按鈕、顯示加載動畫等,減少用戶誤操作的可能性。
  • 錯誤處理:提供友好的錯誤處理機制,當檢測到重復提交時,返回清晰的錯誤信息,并引導用戶重新嘗試或聯(lián)系支持人員。

4. 性能考慮

  • 性能開銷: 防重復提交的檢查會增加一定的性能開銷,特別是在高并發(fā)場景下。確保這些檢查不會成為系統(tǒng)性能的瓶頸。
  • 緩存策略:使用緩存來存儲時間戳信息,減少對數據庫或會話的頻繁訪問,提高性能。

5. 安全性

  • 會話管理: 確保會話管理的安全性,防止會話劫持等攻擊。
  • 時間戳驗證: 驗證時間戳的有效性和合法性,防止惡意用戶篡改時間戳。

6. 日志記錄

  • 日志記錄: 記錄每次請求的時間戳和處理結果,便于后續(xù)的審計和問題排查。

示例代碼

以下是一個更完善的 @RepeatSubmit 注解和切面實現,考慮了上述注意事項:

定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
    String value() default "";
    int interval() default 3000; // 默認3秒
    String message() default "請勿重復提交";
}
// 創(chuàng)建切面
@Aspect
@Component
public class RepeatSubmitAspect {

    @Autowired
    private RedisTemplate<String, Long> redisTemplate;

    @Around("@annotation(repeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        // 獲取方法簽名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 獲取請求上下文
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // 獲取注解參數
        int interval = repeatSubmit.interval();
        String message = repeatSubmit.message();

        // 生成唯一的請求標識
        String key = method.getName() + ":" + request.getRemoteAddr();

        // 從 Redis 中獲取上次請求的時間戳
        Long lastRequestTime = redisTemplate.opsForValue().get(key);

        // 檢查是否重復提交
        if (lastRequestTime != null && System.currentTimeMillis() - lastRequestTime < interval) {
            throw new RuntimeException(message);
        }

        // 記錄當前請求的時間戳
        redisTemplate.opsForValue().set(key, System.currentTimeMillis(), interval, TimeUnit.MILLISECONDS);

        // 繼續(xù)執(zhí)行目標方法
        return joinPoint.proceed();
    }
}
// 在控制器方法中使用注解
@RestController
public class UserController {

    @PostMapping("/submitForm")
    @RepeatSubmit(interval = 5000, message = "請等待5秒后再提交")
    public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
        // 處理表單提交邏輯
        return ResponseEntity.ok("表單提交成功");
    }
}

控制流圖

以下是 @RepeatSubmit 注解的控制流圖,展示了其工作原理:

flowchart TD
    A[開始] --> B[接收請求]
    B --> C[生成唯一請求標識]
    C --> D[從 Redis 獲取上次請求時間戳]
    D -->|存在且未過期| E[返回重復提交提示信息]
    D -->|不存在或已過期| F[記錄當前請求時間戳]
    F --> G[繼續(xù)執(zhí)行目標方法]
    G --> H[返回成功響應]
    H --> I[結束]

說明

  • A: 開始處理請求。
  • B: 接收到客戶端的請求。
  • C: 生成唯一的請求標識,通常包括方法名和客戶端 IP 地址。
  • D: 從 Redis 中獲取上次請求的時間戳。
  • E: 如果存在且未過期,返回重復提交提示信息。
  • F: 如果不存在或已過期,記錄當前請求的時間戳。
  • G: 繼續(xù)執(zhí)行目標方法。
  • H: 請求處理成功后,返回成功響應。
  • I: 結束請求處理過程。

使用@RepeatSubmit會失效嗎

使用 @RepeatSubmit 注解來防止表單重復提交時,確實可能會遇到一些情況下失效的問題。

以下是一些常見的失效原因及解決方案:

1. 前端快速連續(xù)點擊

  • 原因: 用戶在短時間內快速連續(xù)點擊提交按鈕,導致后端無法及時響應和處理。
  • 解決方案: 前端禁用按鈕:
  • 在用戶點擊提交按鈕后,立即禁用按鈕,防止多次點擊。 前端顯示加載動畫: 顯示加載動畫,告知用戶請求正在處理中。

2. 網絡延遲

  • 原因: 網絡延遲可能導致用戶認為請求失敗,從而再次提交。
  • 解決方案: 前端超時提示: 設置合理的請求超時時間,并在超時后提示用戶。
  • 后端重試機制: 在后端實現重試機制,但需謹慎處理,避免無限重試。

3. 會話失效

  • 原因: 如果使用會話(Session)來存儲時間戳,會話可能因超時或服務器重啟而失效。
  • 解決方案: 使用分布式緩存: 使用 Redis等分布式緩存來存儲時間戳,確保在多服務器環(huán)境下也能正常工作。

4. 并發(fā)請求

  • 原因: 在高并發(fā)環(huán)境下,多個請求可能同時到達,導致時間戳檢查失效。
  • 解決方案: 線程安全: 使用線程安全的數據結構(如ConcurrentHashMap 或 AtomicLong)來存儲時間戳。
  • 分布式鎖: 在分布式環(huán)境下,使用分布式鎖(如 Redis分布式鎖)來確保時間戳的讀取和寫入操作是原子性的。

5. 時間戳精度問題

  • 原因: 時間戳的精度可能不夠高,導致短時間內多次請求被視為同一請求。
  • 解決方案: 提高時間戳精度:使用更高精度的時間戳(如納秒)來減少沖突。

6. 代碼邏輯錯誤

  • 原因: 切面或攔截器的邏輯錯誤可能導致 @RepeatSubmit 注解失效。
  • 解決方案: 代碼審查:仔細審查切面或攔截器的代碼,確保邏輯正確。
  • 單元測試: 編寫單元測試,覆蓋各種邊界情況,確保 @RepeatSubmit 注解按預期工作。

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 舉例講解Java中synchronized關鍵字的用法

    舉例講解Java中synchronized關鍵字的用法

    這篇文章主要介紹了Java中synchronized關鍵字的用法,針對synchronized修飾方法的使用作出了簡單講解和演示,需要的朋友可以參考下
    2016-04-04
  • Java實現單例模式的五種方法介紹

    Java實現單例模式的五種方法介紹

    單例模式確保某個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。在計算機系統(tǒng)中,線程池、緩存、日志對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例
    2023-01-01
  • java實現學生宿舍系統(tǒng)

    java實現學生宿舍系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現學生宿舍系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • SpringBoot jar包大小優(yōu)化問題及解決

    SpringBoot jar包大小優(yōu)化問題及解決

    這篇文章主要介紹了SpringBoot jar包大小優(yōu)化問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • IDEA使用Maven創(chuàng)建父與子多模塊項目的圖文教程

    IDEA使用Maven創(chuàng)建父與子多模塊項目的圖文教程

    在?IntelliJ?IDEA?中使用?Maven?創(chuàng)建父與子多模塊項目是一個常見的開發(fā)實踐,有助于更好地組織和管理代碼,所以本文小編給大家介紹了IDEA使用Maven創(chuàng)建父與子多模塊項目的圖文教程,需要的小伙伴跟著小編一起來看看吧
    2025-03-03
  • RocketMQ集群消費與廣播消費模式

    RocketMQ集群消費與廣播消費模式

    這篇文章主要介紹了RocketMQ集群消費與廣播消費模式,消息隊列RocketMQ版支持集群消費和廣播消費,本文介紹集群消費和廣播消費的基本概念、適用場景、功能差異、注意事項以及設置方式
    2023-02-02
  • MyBatis使用Zookeeper保存數據庫的配置可動態(tài)刷新的實現代碼

    MyBatis使用Zookeeper保存數據庫的配置可動態(tài)刷新的實現代碼

    這篇文章主要介紹了MyBatis使用Zookeeper保存數據庫的配置,可動態(tài)刷新,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • selenium+java破解極驗滑動驗證碼的示例代碼

    selenium+java破解極驗滑動驗證碼的示例代碼

    本篇文章主要介紹了selenium+java破解極驗滑動驗證碼的示例代碼,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 關于Lambda表達式的方法引用和構造器引用簡的單示例

    關于Lambda表達式的方法引用和構造器引用簡的單示例

    這篇文章主要介紹了關于Lambda表達式的方法引用和構造器引用簡的單示例,方法引用與構造器引用可以使?Lambda?表達式的代碼塊更加簡潔<BR>,需要的朋友可以參考下
    2023-04-04
  • Jasypt對SpringBoot配置文件加密

    Jasypt對SpringBoot配置文件加密

    數據庫密碼直接明文寫在配置中,對安全來說,是一個很大的挑戰(zhàn)。一旦密碼泄漏,將會帶來很大的安全隱患。尤其在一些企業(yè)對安全性要求很高,因此我們就考慮如何對密碼進行加密。本文著重介紹Jasypt對SpringBoot配置文件加密。
    2021-05-05

最新評論