日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Boot 使用 AOP 防止重复提交

發布時間:2025/3/21 javascript 11 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Boot 使用 AOP 防止重复提交 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在傳統的web項目中,防止重復提交,通常做法是:后端生成一個唯一的提交令牌(uuid),并存儲在服務端。頁面提交請求攜帶這個提交令牌,后端驗證并在第一次驗證后刪除該令牌,保證提交請求的唯一性。

上述的思路其實沒有問題的,但是需要前后端都稍加改動,如果在業務開發完在加這個的話,改動量未免有些大了,本節的實現方案無需前端配合,純后端處理。

思路

  • 自定義注解?@NoRepeatSubmit?標記所有Controller中的提交請求

  • 通過AOP 對所有標記了?@NoRepeatSubmit?的方法攔截

  • 在業務方法執行前,獲取當前用戶的 token(或者JSessionId)+ 當前請求地址,作為一個唯一 KEY,去獲取 Redis 分布式鎖(如果此時并發獲取,只有一個線程會成功獲取鎖)

  • 業務方法執行后,釋放鎖

  • 關于Redis 分布式鎖

    • 不了解的同學戳這里 ==>?Redis分布式鎖的正確實現方式

    • 使用Redis 是為了在負載均衡部署,如果是單機的部署的項目可以使用一個線程安全的本地Cache 替代 Redis

    Code

    這里只貼出 AOP 類和測試類,完整代碼見 ==>?Gitee

    @Aspect @Component public class RepeatSubmitAspect {private final static Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);@Autowiredprivate RedisLock redisLock;@Pointcut("@annotation(noRepeatSubmit)")public void pointCut(NoRepeatSubmit noRepeatSubmit) {}@Around("pointCut(noRepeatSubmit)")public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {int lockSeconds = noRepeatSubmit.lockTime();HttpServletRequest request = RequestUtils.getRequest();Assert.notNull(request, "request can not null");// 此處可以用token或者JSessionIdString token = request.getHeader("Authorization");String path = request.getServletPath();String key = getKey(token, path);String clientId = getClientId();boolean isSuccess = redisLock.tryLock(key, clientId, lockSeconds);if (isSuccess) {LOGGER.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);// 獲取鎖成功, 執行進程Object result;try {result = pjp.proceed();} finally {// 解鎖redisLock.releaseLock(key, clientId);LOGGER.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);}return result;} else {// 獲取鎖失敗,認為是重復提交的請求LOGGER.info("tryLock fail, key = [{}]", key);return new ResultBean(ResultBean.FAIL, "重復請求,請稍后再試", null);}}private String getKey(String token, String path) {return token + path;}private String getClientId() {return UUID.randomUUID().toString();}}

    多線程測試

    測試代碼如下,模擬十個請求并發同時提交

    @Component public class RunTest implements ApplicationRunner {private static final Logger LOGGER = LoggerFactory.getLogger(RunTest.class);@Autowiredprivate RestTemplate restTemplate;@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("執行多線程測試");String url="http://localhost:8000/submit";CountDownLatch countDownLatch = new CountDownLatch(1);ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0; i<10; i++){String userId = "userId" + i;HttpEntity request = buildRequest(userId);executorService.submit(() -> {try {countDownLatch.await();System.out.println("Thread:"+Thread.currentThread().getName()+", time:"+System.currentTimeMillis());ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);System.out.println("Thread:"+Thread.currentThread().getName() + "," + response.getBody());} catch (InterruptedException e) {e.printStackTrace();}});}countDownLatch.countDown();}private HttpEntity buildRequest(String userId) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);headers.set("Authorization", "yourToken");Map<String, Object> body = new HashMap<>();body.put("userId", userId);return new HttpEntity<>(body, headers);}}

    成功防止重復提交,控制臺日志如下,可以看到十個線程的啟動時間幾乎同時發起,只有一個請求提交成功了

    總結

    以上是生活随笔為你收集整理的Spring Boot 使用 AOP 防止重复提交的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。