最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3+Springboot實(shí)現(xiàn)前后端防抖增強(qiáng)的示例代碼

 更新時(shí)間:2025年06月26日 09:06:05   作者:BillKu  
這篇文章主要為大家詳細(xì)介紹了Vue3如何結(jié)合Springboot實(shí)現(xiàn)前后端防抖增強(qiáng)功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

我將基于您提供的防抖切面代碼進(jìn)行改進(jìn),主要優(yōu)化點(diǎn)包括使用Redis實(shí)現(xiàn)分布式防抖、增強(qiáng)鍵生成策略、改進(jìn)異常處理等。以下是完整的改進(jìn)方案:

1. 改進(jìn)的防抖注解 (Debounce.java)

package com.weiyu.anno;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Debounce {

    /**
     * 防抖時(shí)間(毫秒),默認(rèn)5秒
     */
    long value() default 5000;

    /**
     * 自定義鍵值(支持SpEL表達(dá)式)
     */
    String key() default "";

    /**
     * 防抖鍵類型
     */
    KeyType keyType() default KeyType.METHOD;

    /**
     * 錯(cuò)誤提示信息
     */
    String message() default "操作過于頻繁,請(qǐng)稍后再試";

    enum KeyType {
        METHOD,     // 方法級(jí)別防抖(默認(rèn))
        USER,       // 用戶級(jí)別防抖(需要用戶上下文)
        IP,         // IP級(jí)別防抖
        PARAM,      // 參數(shù)級(jí)別防抖(基于參數(shù)值)
        CUSTOM      // 完全自定義鍵
    }
}

2. 改進(jìn)的防抖工具類 (DebounceUtil.java)

package com.weiyu.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

@Component
public class DebounceUtil {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final ExpressionParser parser = new SpelExpressionParser();

    /**
     * 檢查并設(shè)置防抖鎖
     * @param key 防抖鍵
     * @param debounceTime 防抖時(shí)間(毫秒)
     * @return 是否允許操作(true:允許,false:防抖中)
     */
    public boolean checkAndSet(String key, long debounceTime) {
        // 使用SETNX+EXPIRE原子操作
        Boolean result = redisTemplate.opsForValue().setIfAbsent(
            key, 
            "1", 
            debounceTime, 
            TimeUnit.MILLISECONDS
        );
        return result != null && result;
    }

    /**
     * 生成防抖鍵
     * @param joinPoint 切點(diǎn)
     * @param keyType 鍵類型
     * @param customKey 自定義鍵表達(dá)式
     * @return 生成的防抖鍵
     */
    public String generateKey(ProceedingJoinPoint joinPoint, Debounce.KeyType keyType, String customKey) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getDeclaringTypeName() + "#" + signature.getName();
        
        // 1. 處理自定義SpEL表達(dá)式
        if (keyType == Debounce.KeyType.CUSTOM && StringUtils.hasText(customKey)) {
            return evaluateSpEL(joinPoint, customKey);
        }
        
        // 2. 處理其他鍵類型
        switch (keyType) {
            case USER:
                return "DEBOUNCE:USER:" + getCurrentUserId() + ":" + methodName;
            case IP:
                return "DEBOUNCE:IP:" + getClientIp() + ":" + methodName;
            case PARAM:
                return "DEBOUNCE:PARAM:" + methodName + ":" + Arrays.hashCode(joinPoint.getArgs());
            case CUSTOM: // 已處理,此處為fallback
            case METHOD:
            default:
                return "DEBOUNCE:METHOD:" + methodName;
        }
    }

    /**
     * 解析SpEL表達(dá)式
     */
    private String evaluateSpEL(ProceedingJoinPoint joinPoint, String expression) {
        try {
            StandardEvaluationContext context = new StandardEvaluationContext();
            
            // 設(shè)置方法參數(shù)
            context.setVariable("args", joinPoint.getArgs());
            
            // 設(shè)置方法簽名
            context.setVariable("method", joinPoint.getSignature());
            
            // 設(shè)置目標(biāo)對(duì)象
            context.setVariable("target", joinPoint.getTarget());
            
            Expression exp = parser.parseExpression(expression);
            return "DEBOUNCE:CUSTOM:" + exp.getValue(context, String.class);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid SpEL expression: " + expression, e);
        }
    }

    /**
     * 獲取當(dāng)前用戶ID(需要根據(jù)實(shí)際認(rèn)證系統(tǒng)實(shí)現(xiàn))
     */
    private String getCurrentUserId() {
        // 示例:從Spring Security上下文中獲取
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            return authentication.getName();
        }
        return "ANONYMOUS";
    }

    /**
     * 獲取客戶端IP
     */
    private String getClientIp() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

3. 改進(jìn)的防抖切面 (DebounceAspect.java)

package com.weiyu.aop;

import com.weiyu.anno.Debounce;
import com.weiyu.exception.DebounceException;
import com.weiyu.utils.DebounceUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;

import java.lang.reflect.Method;

/**
 * 改進(jìn)的防抖切面(支持Redis分布式鎖)
 */
@Aspect
@Component
public class DebounceAspect {

    @Autowired
    private DebounceUtil debounceUtil;

    @Around("@annotation(debounceAnnotation)")
    public Object debounce(ProceedingJoinPoint joinPoint, Debounce debounceAnnotation) throws Throwable {
        // 獲取方法上的注解(處理繼承情況)
        if (debounceAnnotation == null) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            debounceAnnotation = AnnotationUtils.findAnnotation(method, Debounce.class);
            if (debounceAnnotation == null) {
                return joinPoint.proceed();
            }
        }

        // 生成防抖鍵
        String key = debounceUtil.generateKey(
            joinPoint,
            debounceAnnotation.keyType(),
            debounceAnnotation.key()
        );

        // 獲取防抖時(shí)間
        long debounceTime = debounceAnnotation.value();

        // 檢查并設(shè)置防抖鎖
        if (!debounceUtil.checkAndSet(key, debounceTime)) {
            throw new DebounceException(debounceAnnotation.message());
        }

        // 執(zhí)行目標(biāo)方法
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            // 異常時(shí)移除防抖鎖(可選)
            // debounceUtil.removeLock(key);
            throw throwable;
        }
    }
}

4. 自定義防抖異常 (DebounceException.java)

package com.weiyu.exception;

/**
 * 防抖業(yè)務(wù)異常
 */
public class DebounceException extends RuntimeException {
    private int code = 429; // 使用429狀態(tài)碼表示操作過于頻繁
    
    public DebounceException(String message) {
        super(message);
    }
    
    public int getCode() {
        return code;
    }
}

5. 全局異常處理器 (GlobalExceptionHandler.java)

package com.weiyu.exception;

import com.weiyu.common.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 處理防抖異常
     */
    @ExceptionHandler(DebounceException.class)
    public Result handleDebounceException(DebounceException e) {
        return Result.error(e.getCode(), e.getMessage());
    }
}

6. 統(tǒng)一響應(yīng)對(duì)象 (Result.java)

package com.weiyu.common;

public class Result<T> {
    private int code;
    private String msg;
    private T data;
    
    // 成功靜態(tài)方法
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("操作成功");
        result.setData(data);
        return result;
    }
    
    // 錯(cuò)誤靜態(tài)方法
    public static <T> Result<T> error(int code, String msg) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
    
    // Getters and Setters
}

7. 使用示例

@RestController
@RequestMapping("/api/download")
public class DownloadController {

    // 基本方法級(jí)別防抖(5秒內(nèi)只能調(diào)用一次)
    @Debounce
    @GetMapping("/basic")
    public Result<String> basicDownload() {
        // ... 下載邏輯
        return Result.success("下載成功");
    }

    // 用戶級(jí)別防抖(每個(gè)用戶5秒內(nèi)只能下載一次)
    @Debounce(keyType = Debounce.KeyType.USER, value = 5000)
    @GetMapping("/user")
    public Result<String> userSpecificDownload() {
        // ... 下載邏輯
        return Result.success("下載成功");
    }

    // 自定義鍵防抖(基于文件ID)
    @Debounce(
        keyType = Debounce.KeyType.CUSTOM,
        key = "'FILE:' + #fileId", // SpEL表達(dá)式
        message = "該文件正在下載中,請(qǐng)稍后再試"
    )
    @GetMapping("/file/{fileId}")
    public Result<String> fileDownload(@PathVariable String fileId) {
        // ... 下載邏輯
        return Result.success("下載成功");
    }

    // IP級(jí)別防抖(每個(gè)IP 10秒內(nèi)只能下載一次)
    @Debounce(keyType = Debounce.KeyType.IP, value = 10000)
    @GetMapping("/ip-based")
    public Result<String> ipBasedDownload() {
        // ... 下載邏輯
        return Result.success("下載成功");
    }
}

8. 前端適配方案

// 在響應(yīng)攔截器中處理防抖異常
instance.interceptors.response.use(
  response => {
    // ... 正常處理
  },
  error => {
    if (error.response) {
      // 識(shí)別防抖異常(HTTP 429狀態(tài)碼)
      if (error.response.status === 429) {
        ElMessage.error(error.response.data?.msg || "操作過于頻繁,請(qǐng)稍后再試");
        return Promise.reject(error);
      }
      
      // 識(shí)別業(yè)務(wù)層防抖異常(code=429)
      const res = error.response.data;
      if (res && res.code === 429) {
        ElMessage.error(res.msg || "操作過于頻繁,請(qǐng)稍后再試");
        return Promise.reject(error);
      }
    }
    // ... 其他錯(cuò)誤處理
  }
);

改進(jìn)亮點(diǎn)

1.分布式支持

  • 使用Redis實(shí)現(xiàn)分布式防抖鎖
  • 原子操作(SETNX + EXPIRE)確保并發(fā)安全

2.靈活的鍵策略

  • 支持方法、用戶、IP、參數(shù)、自定義五種鍵類型
  • 集成SpEL表達(dá)式引擎實(shí)現(xiàn)動(dòng)態(tài)鍵生成

3.增強(qiáng)的異常處理

  • 自定義防抖異常類型(DebounceException)
  • 統(tǒng)一使用429狀態(tài)碼(Too Many Requests)
  • 可自定義錯(cuò)誤消息

4.注解增強(qiáng)

  • 支持方法繼承的注解查找
  • 可配置防抖時(shí)間和錯(cuò)誤消息
  • 支持SpEL表達(dá)式自定義鍵

5.集成Spring生態(tài)

  • 自動(dòng)獲取請(qǐng)求上下文(IP、用戶信息)
  • 通過全局異常處理器統(tǒng)一處理

6.更精確的防抖

  • 精確到毫秒級(jí)別的防抖控制
  • 避免本地內(nèi)存防抖的分布式問題

這個(gè)改進(jìn)方案保持了原有代碼的結(jié)構(gòu)和風(fēng)格,同時(shí)增加了企業(yè)級(jí)應(yīng)用所需的分布式支持和靈活性,特別適合在微服務(wù)架構(gòu)中使用。

以上就是Vue3+Springboot實(shí)現(xiàn)前后端防抖增強(qiáng)的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Vue Springboot防抖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue?+elementui?項(xiàng)目登錄通過不同賬號(hào)切換側(cè)邊欄菜單的顏色

    vue?+elementui?項(xiàng)目登錄通過不同賬號(hào)切換側(cè)邊欄菜單的顏色

    這篇文章主要介紹了vue?+elementui?項(xiàng)目登錄通過不同賬號(hào)切換側(cè)邊欄菜單的顏色,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • 詳解vue-cli多頁面工程實(shí)踐

    詳解vue-cli多頁面工程實(shí)踐

    本篇文章主要介紹了詳解vue-cli多頁面工程實(shí)踐,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 如何用VUE和Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類小游戲

    如何用VUE和Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類小游戲

    這篇文章主要介紹了如何用VUE和Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類小游戲,麻雀雖小,五臟俱全,對(duì)游戲感興趣的同學(xué),可以參考下,研究里面的原理和實(shí)現(xiàn)方法
    2021-04-04
  • 前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法

    前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法

    這篇文章主要介紹了前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Vuex 入門教程

    Vuex 入門教程

    這篇文章主要介紹了Vuex 入門教程,Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式, 那這個(gè) vuex 怎么用呢?就具體來看一下吧
    2018-01-01
  • vue3中的props組件抽離

    vue3中的props組件抽離

    這篇文章主要介紹了vue3中的props組件抽離,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue如何在store倉庫中使用路由

    vue如何在store倉庫中使用路由

    這篇文章主要介紹了vue如何在store倉庫中使用路由,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Vue 子組件更新props中的屬性值問題

    Vue 子組件更新props中的屬性值問題

    這篇文章主要介紹了Vue 子組件更新props中的屬性值問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 在vue中多次調(diào)用同一個(gè)定義全局變量的實(shí)例

    在vue中多次調(diào)用同一個(gè)定義全局變量的實(shí)例

    今天小編就為大家分享一篇在vue中多次調(diào)用同一個(gè)定義全局變量的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • Nuxt.js之自動(dòng)路由原理的實(shí)現(xiàn)方法

    Nuxt.js之自動(dòng)路由原理的實(shí)現(xiàn)方法

    這篇文章主要介紹了Nuxt.js之自動(dòng)路由原理的實(shí)現(xiàn)方法,nuxt.js會(huì)根據(jù)pages目錄結(jié)構(gòu)自動(dòng)生成vue-router模塊的路由配置。非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-11-11

最新評(píng)論

同心县| 富裕县| 枞阳县| 舒城县| 彰武县| 华坪县| 桐梓县| 泸定县| 新巴尔虎左旗| 岗巴县| 毕节市| 兴业县| 会东县| 龙山县| 额尔古纳市| 大竹县| 资兴市| 西吉县| 偏关县| 彭山县| 新沂市| 徐州市| 汶川县| 汪清县| 密山市| 清水县| 马山县| 苏尼特右旗| 舟山市| 贵溪市| 文山县| 双鸭山市| 乳源| 秭归县| 昌图县| 黑山县| 出国| 乌恰县| 辽阳市| 固阳县| 托里县|