国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交

這篇具有很好參考價(jià)值的文章主要介紹了springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

?springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交,spring boot實(shí)戰(zhàn),spring boot,redis,java

??個(gè)人主頁:? ? ?蒾酒

??系列專欄:《spring boot實(shí)戰(zhàn)》

??山高路遠(yuǎn),行路漫漫,終有歸途


目錄

寫在前面

實(shí)現(xiàn)思路

實(shí)現(xiàn)步驟

1.定義防重復(fù)提交注解

2.編寫一個(gè)切面去發(fā)現(xiàn)該注解然后執(zhí)行防重復(fù)提交邏輯

3.測(cè)試

依賴條件

1.接口上標(biāo)記防重復(fù)提交注解

2.接口測(cè)試

寫在最后


寫在前面

本文介紹了springboot開發(fā)后端服務(wù)中,防重復(fù)提交功能的設(shè)計(jì)與實(shí)現(xiàn),堅(jiān)持看完相信對(duì)你有幫助。

同時(shí)歡迎訂閱springboot系列專欄,持續(xù)分享spring boot的使用經(jīng)驗(yàn)。

實(shí)現(xiàn)思路

通過定義一個(gè)防重復(fù)提交的自定義注解,再通過AOP的前置通知攔截帶有該注解的方法,執(zhí)行防重復(fù)提交邏輯,需要拼接一個(gè)唯一的key,如果redis中不存在則代表第一次請(qǐng)求,將這個(gè)key存入redis,設(shè)置注解類中指定的過期時(shí)間,遇到下次重復(fù)提交請(qǐng)求,直接拋出對(duì)應(yīng)異常,全局異常處理返回對(duì)應(yīng)信息即可。

需要注意

這個(gè)key的生成需要考慮有token和無token情況,同時(shí)滿足唯一性。

  • 有 token;可以用 token+請(qǐng)求參數(shù),做為唯一值!
  • 無 token:可以用請(qǐng)求路徑+請(qǐng)求參數(shù),做為唯一值!

實(shí)現(xiàn)步驟

1.定義防重復(fù)提交注解

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * @author mijiupro
 */
@Inherited
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {

    /**
     * 鎖定時(shí)間,默認(rèn)5000毫秒
     */
    int interval() default 5000;

    /**
     * 鎖定時(shí)間單位,默認(rèn)毫秒
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

    /**
     * 提示信息
     */
    String message() default "不允許重復(fù)提交,請(qǐng)稍后再試!";

}

2.編寫一個(gè)切面去發(fā)現(xiàn)該注解然后執(zhí)行防重復(fù)提交邏輯

因?yàn)榫彺娴膋ey有拼接請(qǐng)求參數(shù),所以遇到文件類型的參數(shù)需要進(jìn)行過濾,拼接邏輯以及參數(shù)過濾方法都在下面代碼中。

import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.mijiu.commom.aop.annotation.RepeatSubmit;
import com.mijiu.commom.exception.GeneralBusinessException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.util.Collection;
import java.util.Map;
import java.util.Objects;

/**
 * @author mijiupro
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {
    private final StringRedisTemplate redisTemplate;

    public RepeatSubmitAspect(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Before("@annotation(repeatSubmit)")
    public void before(JoinPoint joinPoint, RepeatSubmit repeatSubmit) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = null;

        if (attributes != null) {
            request = attributes.getRequest();
        }

        //請(qǐng)求參數(shù)拼接
        String requestParams = argsArrayToString(joinPoint.getArgs());

        String authorizationHeader = null;
        if (request != null) {
            authorizationHeader = request.getHeader("Authorization");
        }

        String submitKey = null;

        if (authorizationHeader != null) {
            //如果存在token則通過token+請(qǐng)求參數(shù)生成唯一標(biāo)識(shí)
            String token = StringUtils.removeStart(authorizationHeader, "Bearer ");
            submitKey= SecureUtil.md5(token+":"+requestParams);

        } else{
            //不存在token則通過請(qǐng)求url+參數(shù)生成唯一標(biāo)識(shí)
            if (request != null) {
                submitKey = SecureUtil.md5(request.getRequestURL().toString()+":"+requestParams);
            }
        }
        //緩存key
        String cacheKey = "repeat_submit:"+submitKey;

        if (Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey))) {
            throw new GeneralBusinessException(repeatSubmit.message());
        }
        redisTemplate.opsForValue().set(cacheKey, "1", repeatSubmit.interval(), repeatSubmit.timeUnit());
    }


    /**
     * 參數(shù)拼接
     * @param args  參數(shù)數(shù)組
     * @return 拼接后的字符串
     */
    private String argsArrayToString(Object[] args){
        StringBuilder params = new StringBuilder();
        if(args!= null && args.length > 0){
            for(Object o:args){
                if(Objects.nonNull(o)&&!isFilterObject(o)){
                    try {
                        params.append(JSONUtil.toJsonStr(o)).append(" ");
                    }catch (Exception e){
                        log.error("參數(shù)拼接異常:{}",e.getMessage());
                    }
                }
            }
        }
        return params.toString().trim();
    }

    /**
     * 判斷是否需要過濾的對(duì)象。
     * @param o  對(duì)象
     * @return true:需要過濾;false:不需要過濾
     */
    private boolean isFilterObject(final Object o) {
        Class<?> c = o.getClass();
        //如果是數(shù)組且類型為文件類型的需要過濾
        if(c.isArray()){
            return  c.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        //如果是集合且類型為文件類型的需要過濾
        else if(Collection.class.isAssignableFrom(c)){
            Collection collection = (Collection) o;
            for(Object value:collection){
                return value instanceof MultipartFile;
            }
        }
        //如果是Map且類型為文件類型的需要過濾
        else if(Map.class.isAssignableFrom(c)){
            Map map = (Map) o;
            for(Object value:map.entrySet()){
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        //如果是文件類型的需要過濾
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }

}

3.測(cè)試

依賴條件

redis:

Spring Boot3整合Redis_springboot3整合redis-CSDN博客https://blog.csdn.net/qq_62262918/article/details/136067550?spm=1001.2014.3001.5502

全局異常捕獲:

Spring Boot3自定義異常及全局異常捕獲_全局異常捕獲 自定義異常-CSDN博客https://blog.csdn.net/qq_62262918/article/details/136110267?spm=1001.2014.3001.5502

swagger3:

Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客https://blog.csdn.net/qq_62262918/article/details/135761392?spm=1001.2014.3001.5502

hutool工具包:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.25</version>
</dependency>

1.接口上標(biāo)記防重復(fù)提交注解

隨便寫個(gè)測(cè)試接口添加防重復(fù)提交注解設(shè)置間隔5000毫秒

    @PostMapping("/add")
    @RepeatSubmit(interval= 5000)
    public void test(@RequestBody User user){
        //添加用戶的操作邏輯。。。
    }

2.接口測(cè)試

第一次提交

springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交,spring boot實(shí)戰(zhàn),spring boot,redis,java

可以看到對(duì)應(yīng)緩存已經(jīng)存入redis了

springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交,spring boot實(shí)戰(zhàn),spring boot,redis,java

5s內(nèi)第二次提交

springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交,spring boot實(shí)戰(zhàn),spring boot,redis,java

寫在最后

springboot使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交到這里就結(jié)束了,本文介紹了一種通用的防重復(fù)提交的實(shí)現(xiàn)方式,代碼邏輯清晰。任何問題評(píng)論區(qū)或私信討論,歡迎指正。文章來源地址http://www.zghlxwxcb.cn/news/detail-847983.html

到了這里,關(guān)于springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • redis + AOP + 自定義注解實(shí)現(xiàn)接口限流

    限流(rate limiting) ? 是指在一定時(shí)間內(nèi),對(duì)某些資源的訪問次數(shù)進(jìn)行限制,以避免資源被濫用或過度消耗。限流可以防止服務(wù)器崩潰、保證用戶體驗(yàn)、提高系統(tǒng)可用性。 限流的方法有很多種,常見的有以下幾種: 漏桶算法: ?漏桶算法通過一個(gè)固定大小的漏桶來模擬流量

    2024年02月03日
    瀏覽(22)
  • springboot aop 自定義注解形式

    springboot aop 自定義注解形式

    2024年01月25日
    瀏覽(19)
  • SpringBoot3自動(dòng)配置流程 SPI機(jī)制 核心注解 自定義starter

    SpringBoot3自動(dòng)配置流程 SPI機(jī)制 核心注解 自定義starter

    導(dǎo)入 starter 依賴導(dǎo)入 autoconfigure 尋找類路徑下 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件 啟動(dòng),加載所有 自動(dòng)配置類 xxxAutoConfiguration 給容器中配置功能 組件 組件參數(shù) 綁定到 屬性類 中。 xxxProperties 屬性類 和 配置文件 前綴項(xiàng)綁定 @Contional 派生的條件

    2024年02月16日
    瀏覽(22)
  • SpringBoot + 自定義注解 + AOP 打造通用開關(guān)

    SpringBoot + 自定義注解 + AOP 打造通用開關(guān)

    前言 最近在工作中遷移代碼的時(shí)候發(fā)現(xiàn)了以前自己寫的一個(gè)通用開關(guān)實(shí)現(xiàn),發(fā)現(xiàn)挺不錯(cuò),特地拿出來分享給大家。 為了有良好的演示效果,我特地重新建了一個(gè)項(xiàng)目,把核心代碼提煉出來加上了更多注釋說明,希望xdm喜歡。 案例 1、項(xiàng)目結(jié)構(gòu) 2、引入依賴 3、yml配置 連接Re

    2024年01月23日
    瀏覽(17)
  • SpringBoot+自定義注解+AOP高級(jí)玩法打造通用開關(guān)

    SpringBoot+自定義注解+AOP高級(jí)玩法打造通用開關(guān)

    1.項(xiàng)目結(jié)構(gòu) 2.引入依賴 3.yml配置 4.自定義注解 5.定義常量 6.AOP核心實(shí)現(xiàn) 7.使用注解 8.工具類 9.測(cè)試接口 10.Redis中把開關(guān)加上 11.啟動(dòng)服務(wù) 將redis中開關(guān)置為1 歡迎大家積極留言交流學(xué)習(xí)心得,點(diǎn)贊的人最美麗!

    2024年02月07日
    瀏覽(19)
  • 【SpringMVC】自定義注解與AOP結(jié)合使用

    【SpringMVC】自定義注解與AOP結(jié)合使用

    目錄 一、SpringMVC之自定義注解 1.1 Java注解簡(jiǎn)介 1.2 為什么要用注解 1.3 注解的分類 ? 1.3.1 JDK基本注解 1.3.2 JDK元注解? 1.3.3 自定義注解? 1.4 自定義注解三種使用案例 1.4.1?案例一(獲取類與方法上的注解值) 1.4.2?案例二(獲取類屬性上的注解屬性值) 1.4.3 案例三(獲取參數(shù)

    2024年02月07日
    瀏覽(26)
  • 【Spring】使用自定義注解方式實(shí)現(xiàn)AOP鑒權(quán)

    AOP,是一種面向切面編程,可以通過預(yù)編譯方式和運(yùn)行期間動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。 在軟件開發(fā)中,鑒權(quán)(Authentication)是一項(xiàng)非常重要的安全措施,用于驗(yàn)證用戶身份和權(quán)限。在應(yīng)用程序中,我們通常會(huì)使用AOP(Aspect-Oriented Programming)來實(shí)現(xiàn)鑒權(quán)功能

    2024年02月11日
    瀏覽(20)
  • 【數(shù)據(jù)脫敏方案】不使用 AOP + 注解,使用 SpringBoot+YAML 實(shí)現(xiàn)

    在項(xiàng)目中遇到一個(gè)需求,需要對(duì)交易接口返回結(jié)果中的指定字段進(jìn)行脫敏操作,但又不能使用 AOP+注解 的形式,于是決定使用一種比較笨的方法: 首先將所有需要脫敏字段及其對(duì)應(yīng)脫敏規(guī)則存儲(chǔ)到 Map 中。 在接口返回時(shí),遍歷結(jié)果中的所有字段,判斷字段名在 Map 中是否存在

    2024年03月15日
    瀏覽(25)
  • SpringBoot定義攔截器+自定義注解+Redis實(shí)現(xiàn)接口防刷(限流)

    在攔截器Interceptor中攔截請(qǐng)求 通過地址+請(qǐng)求uri作為調(diào)用者訪問接口的區(qū)分在Redis中進(jìn)行計(jì)數(shù)達(dá)到限流目的 定義參數(shù) 訪問周期 最大訪問次數(shù) 禁用時(shí)長 代碼實(shí)現(xiàn) 定義攔截器:實(shí)現(xiàn)HandlerInterceptor接口,重寫preHandle()方法 注冊(cè)攔截器:配置類實(shí)現(xiàn)WebMvcConfigurer接口,重寫addIntercep

    2024年02月05日
    瀏覽(32)
  • SpringBoot3集成Kafka優(yōu)雅實(shí)現(xiàn)信息消費(fèi)發(fā)送

    ???????首先,你的JDK是否已經(jīng)是8+了呢? ???????其次,你是否已經(jīng)用上SpringBoot3了呢? ???????最后,這次分享的是SpringBoot3下的kafka發(fā)信息與消費(fèi)信息。 ???????這次的場(chǎng)景是springboot3+多數(shù)據(jù)源的數(shù)據(jù)交換中心(數(shù)倉)需要消費(fèi)Kafka里的上游推送信息,這里做數(shù)據(jù)

    2024年02月02日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包