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

springboot aop實(shí)現(xiàn)接口防重復(fù)操作

這篇具有很好參考價(jià)值的文章主要介紹了springboot aop實(shí)現(xiàn)接口防重復(fù)操作。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、前言
有時(shí)在項(xiàng)目開(kāi)發(fā)中某些接口邏輯比較復(fù)雜,響應(yīng)時(shí)間長(zhǎng),那么可能導(dǎo)致重復(fù)提交問(wèn)題。

二、如何解決
1.先定義一個(gè)防重復(fù)提交的注解。

import java.lang.annotation.*;

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

    /**
     * 防重復(fù)操作限時(shí)標(biāo)記數(shù)值(存儲(chǔ)redis限時(shí)標(biāo)記數(shù)值)
     */
    String value() default "value" ;

    /**
     * 防重復(fù)操作過(guò)期時(shí)間(借助redis實(shí)現(xiàn)限時(shí)控制)
     */
    int expireSeconds() default 10;
}

2.編寫(xiě)防重復(fù)操作的AOP

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Slf4j
@Component
@Aspect
@Order(0)
public class NoRepeatSubmitAspect  {
 private static final String TOKENAuthorization = "Authorization";

    private static final String TOKENUSERNAME = "api-userName";

    private static final String PREVENT_DUPLICATION_PREFIX = "PREVENT_DUPLICATION_PREFIX:";

    @Autowired
    private RedisService redisService;

    @Pointcut("@annotation(com.dp.aop.annotation.RepeatSubmit)")
    public void preventDuplication() {}
    @Around("preventDuplication()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        if (Objects.isNull(request)) {
            return joinPoint.proceed();
        }
        //獲取執(zhí)行方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //獲取防重復(fù)提交注解
        RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
        //獲取token以及方法標(biāo)記,生成redisKey
        String header = request.getHeader(TOKENAuthorization);
        String token = header == null ? "" : header;
        String requestHeader = request.getHeader(TOKENUSERNAME);
        String headerToken = requestHeader == null ? "" : requestHeader;
        token = token + headerToken;
        String url = request.getRequestURI();
        // 通過(guò)前綴 + url + token + 函數(shù)參數(shù)簽名 來(lái)生成redis上的 key
        String redisKey = PREVENT_DUPLICATION_PREFIX
                .concat(url)
                .concat(token)
                .concat(getMethodSign(method, joinPoint.getArgs()));
        RedisLock redisLock = null;
        try {
            try {
                redisLock = redisService.tryLock(redisKey, annotation.expireSeconds());
            } catch (Exception e) {
                log.error("tryLock error  ", e);
                throw new BizException(CommonMsgConstants.NoRepeatSubmitMsg);
            }
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("throwable trace is ", throwable);
            throw new RuntimeException(throwable);
        } finally {
            if (Objects.nonNull(redisLock)) {
                redisLock.unlock();
            }
        }
    }
    /**
     * 生成方法標(biāo)記:采用數(shù)字簽名算法SHA1對(duì)方法簽名字符串加簽
     *
     * @param method
     * @param args
     * @return
     */
    private String getMethodSign(Method method, Object... args) {
        StringBuilder sb = new StringBuilder(method.toString());
        for (Object arg : args) {
            sb.append(toString(arg));
        }
        return DigestUtil.sha1Hex(sb.toString());
    }

    private String toString(Object arg) {
        if (Objects.isNull(arg)) {
            return "null";
        }
        if (arg instanceof Number) {
            return arg.toString();
        }
        return JSONObject.toJSONString(arg);
    }

}

3.接下來(lái)定義redisService類(lèi)

@Component
public class RedisService {
	public RedisLock tryLock(String lockKey, int expireTime) {
        String lockValue = UUID.randomUUID().toString();
        Boolean hasLock = (Boolean)this.redisTemplate.execute((connection) -> {
            Object nativeConnection = connection.getNativeConnection();
            String status = null;
            if (nativeConnection instanceof Jedis) {
                Jedis jedis = (Jedis)nativeConnection;
                status = jedis.set(lockKey, lockValue, "nx", "ex", expireTime);
            } else {
                JedisCluster jedisx = (JedisCluster)nativeConnection;
                status = jedisx.set(lockKey, lockValue, "nx", "ex", (long)expireTime);
            }

            return "OK".equals(status);
        });
        if (hasLock) {
            return new RedisService.RedisLockInner(this.redisTemplate, lockKey, lockValue);
        } else {
            throw new RuntimeException("獲取鎖失敗,lockKey:" + lockKey);
        }
    }
 private class RedisLockInner implements RedisLock {
        private RedisTemplate redisTemplate;
        private String key;
        private String expectedValue;

        protected RedisLockInner(RedisTemplate redisTemplate, String key, String expectedValue) {
            this.redisTemplate = redisTemplate;
            this.key = key;
            this.expectedValue = expectedValue;
        }

       public Object unlock() {
            final List<String> keys = new ArrayList();
            keys.add(this.key);
            final List<String> values = new ArrayList();
            values.add(this.expectedValue);
            Object result = this.redisTemplate.execute(new RedisCallback<Long>() {
                public Long doInRedis(RedisConnection connection) throws DataAccessException {
                    Object nativeConnection = connection.getNativeConnection();
                    return nativeConnection instanceof JedisCluster ? (Long)((JedisCluster)nativeConnection).eval("if redis.call('get',KEYS[1])==ARGV[1]\n then\n   return redis.call('del',KEYS[1])\n else\n   return 0\n end", keys, values) : (Long)((Jedis)nativeConnection).eval("if redis.call('get',KEYS[1])==ARGV[1]\n then\n   return redis.call('del',KEYS[1])\n else\n   return 0\n end", keys, values);
                }
            });
           return result;
        }

        public void close() throws Exception {
            this.unlock();
        }
    }
}

4.最后在Controller接口加上注解就行了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-669062.html

到了這里,關(guān)于springboot aop實(shí)現(xiàn)接口防重復(fù)操作的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀(guān)點(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)文章

  • Spring Boot入門(mén)(23):基于AOP實(shí)現(xiàn)自定義注解攔截接口日志并保存入庫(kù) | 超級(jí)詳細(xì),建議收藏

    Spring Boot入門(mén)(23):基于AOP實(shí)現(xiàn)自定義注解攔截接口日志并保存入庫(kù) | 超級(jí)詳細(xì),建議收藏

    ? ? ? ? 在上兩期中,我們著重介紹了如何集成使用 Logback?與?log4j2?日志框架的使用,今天我們講解的主題依舊跟日志有關(guān),不過(guò)不是使用何種開(kāi)源框架,而是自己動(dòng)手造。 ? ? ? ? Spring的核心之一AOP;AOP翻譯過(guò)來(lái)叫面向切面編程, 核心就是這個(gè)切面. 切面表示從業(yè)務(wù)邏輯中

    2024年02月11日
    瀏覽(29)
  • 【springboot】spring的Aop結(jié)合Redis實(shí)現(xiàn)對(duì)短信接口的限流

    【springboot】spring的Aop結(jié)合Redis實(shí)現(xiàn)對(duì)短信接口的限流

    場(chǎng)景: 為了限制短信驗(yàn)證碼接口的訪(fǎng)問(wèn)次數(shù),防止被刷,結(jié)合Aop和redis根據(jù)用戶(hù)ip對(duì)用戶(hù)限流 首先我們創(chuàng)建一個(gè) Spring Boot 工程,引入 Web 和 Redis 依賴(lài),同時(shí)考慮到接口限流一般是通過(guò)注解來(lái)標(biāo)記,而注解是通過(guò) AOP 來(lái)解析的,所以我們還需要加上 AOP 的依賴(lài),最終的依賴(lài)如下:

    2024年02月05日
    瀏覽(21)
  • spring boot 使用AOP+自定義注解+反射實(shí)現(xiàn)操作日志記錄修改前數(shù)據(jù)和修改后對(duì)比數(shù)據(jù),并保存至日志表

    spring boot 使用AOP+自定義注解+反射實(shí)現(xiàn)操作日志記錄修改前數(shù)據(jù)和修改后對(duì)比數(shù)據(jù),并保存至日志表

    使用FieldMeta自定義注解,看個(gè)人業(yè)務(wù)自行覺(jué)得是否需要重新定義實(shí)體 實(shí)現(xiàn)類(lèi) :通過(guò)該實(shí)現(xiàn)類(lèi)獲取更新前后的數(shù)據(jù)。 該實(shí)現(xiàn)類(lèi)的實(shí)現(xiàn)原理為:獲取入?yún)⒊鋈氲膇d值,獲取sqlSessionFactory,通過(guò)sqlSessionFactory獲取selectByPrimaryKey()該方法,執(zhí)行該方法可獲取id對(duì)應(yīng)數(shù)據(jù)更新操作前后的數(shù)

    2024年01月23日
    瀏覽(29)
  • Spring Boot + Aop 記錄用戶(hù)操作日志

    Spring Boot + Aop 記錄用戶(hù)操作日志

    本文主要介紹通過(guò)Aop記錄用戶(hù)操作日志,這也是目前比較常用的用法,由于水平有限,所以可能存在錯(cuò)漏之處,望指正。 對(duì)應(yīng)實(shí)體類(lèi)為 SysOperLog.java MyLog.java BusinessType.java — 操作類(lèi)型枚舉類(lèi) LogAspect.java 用戶(hù)操作日志是AOP最常見(jiàn)的一種業(yè)務(wù)場(chǎng)景,這里只是簡(jiǎn)單記錄了少量信息,

    2024年02月06日
    瀏覽(23)
  • Spring Boot入門(mén)(23):記錄接口日志再也不難!用AOP和自定義注解給Spring Boot加上日志攔截器!

    Spring Boot入門(mén)(23):記錄接口日志再也不難!用AOP和自定義注解給Spring Boot加上日志攔截器!

    ? ? ? ? 在上兩期中,我們著重介紹了如何集成使用 Logback?與?log4j2?日志框架的使用,今天我們講解的主題依舊跟日志有關(guān),不過(guò)不是使用何種開(kāi)源框架,而是自己動(dòng)手造。 ? ? ? ? Spring的核心之一AOP;AOP翻譯過(guò)來(lái)叫面向切面編程, 核心就是這個(gè)切面. 切面表示從業(yè)務(wù)邏輯中

    2024年02月11日
    瀏覽(26)
  • springboot3使用自定義注解+AOP+redis優(yōu)雅實(shí)現(xiàn)防重復(fù)提交

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

    ? ??個(gè)人主頁(yè):? ? ?蒾酒 ?? 系列專(zhuān)欄 :《spring boot實(shí)戰(zhàn)》 ?? 山高路遠(yuǎn),行路漫漫,終有歸途 目錄 寫(xiě)在前面 實(shí)現(xiàn)思路 實(shí)現(xiàn)步驟 1.定義防重復(fù)提交注解 2.編寫(xiě)一個(gè)切面去發(fā)現(xiàn)該注解然后執(zhí)行防重復(fù)提交邏輯 3.測(cè)試 依賴(lài)條件 1.接口上標(biāo)記防重復(fù)提交注解 2.接口測(cè)試 寫(xiě)在最

    2024年04月11日
    瀏覽(23)
  • 使用Spring Boot AOP實(shí)現(xiàn)日志記錄

    使用Spring Boot AOP實(shí)現(xiàn)日志記錄

    目錄 介紹 1.1 什么是AOP 1.2 AOP體系與概念 AOP簡(jiǎn)單實(shí)現(xiàn) 2.1 新建一個(gè)SpringBoot項(xiàng)目,無(wú)需選擇依賴(lài) 2.2 設(shè)置好本地Maven配置后,在pom.xml文件里添加添加maven依賴(lài) 2.3 創(chuàng)建一個(gè)業(yè)務(wù)類(lèi)接口 2.4 在實(shí)體類(lèi)實(shí)現(xiàn)接口業(yè)務(wù)? 2.5 在單元測(cè)試運(yùn)行結(jié)果 2.6?創(chuàng)建切面類(lèi) 2.7?再次運(yùn)行測(cè)試 ?總結(jié) 1.

    2024年02月14日
    瀏覽(27)
  • spring boot 使用AOP實(shí)現(xiàn)是否已登錄檢測(cè)

    ? ? ? ? 前后端分離的開(kāi)發(fā)中,用戶(hù)http請(qǐng)求應(yīng)用服務(wù)的接口時(shí), 如果要求檢測(cè)該用戶(hù)是否已登錄??梢詫?shí)現(xiàn)的方法有多種, 本示例是通過(guò)aop 的方式實(shí)現(xiàn),簡(jiǎn)單有效。 ? ? ? ? 約定:前端http的post?請(qǐng)求 1、在pom.xml 引用 2、創(chuàng)建插入標(biāo)記 3、實(shí)現(xiàn)切入類(lèi) 4 建立api接口,在需要檢

    2024年02月20日
    瀏覽(21)
  • Spring Boot學(xué)習(xí)隨筆- 實(shí)現(xiàn)AOP(JoinPoint、ProceedingJoinPoint、自定義注解類(lèi)實(shí)現(xiàn)切面)

    Spring Boot學(xué)習(xí)隨筆- 實(shí)現(xiàn)AOP(JoinPoint、ProceedingJoinPoint、自定義注解類(lèi)實(shí)現(xiàn)切面)

    學(xué)習(xí)視頻:【編程不良人】2021年SpringBoot最新最全教程 問(wèn)題 現(xiàn)有業(yè)務(wù)層開(kāi)發(fā)存在問(wèn)題 額外功能代碼存在大量冗余 每個(gè)方法都需要書(shū)寫(xiě)一遍額外功能代碼不利于項(xiàng)目維護(hù) Spring中的AOP AOP:Aspect 切面 + Oriented 面向 Programmaing 面向切面編程 Aspect(切面) = Advice(通知) + Pointcut(

    2024年02月04日
    瀏覽(43)
  • 三方開(kāi)放接口,Springboot通過(guò)AOP實(shí)現(xiàn)API接口的簽名驗(yàn)證

    前言 對(duì)外開(kāi)放的接口,需要驗(yàn)證請(qǐng)求方發(fā)送過(guò)來(lái)的數(shù)據(jù)確實(shí)是由發(fā)送方發(fā)起的,并且中途不能被篡改和偽造,所以才會(huì)對(duì)接口的訪(fǎng)問(wèn)進(jìn)行簽名驗(yàn)證,以保證雙方獲取到的原來(lái)的信息是沒(méi)有經(jīng)過(guò)篡改的。 實(shí)現(xiàn)方法 對(duì)請(qǐng)求的信息內(nèi)容,通過(guò)MD5運(yùn)算或者其他算法(必須是 不可逆的

    2024年02月07日
    瀏覽(31)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包