Spring防止重復(fù)點(diǎn)擊的兩種實(shí)現(xiàn)方法
第一種:@EasyLock
簡(jiǎn)介
為了簡(jiǎn)化可復(fù)用注解,自己實(shí)現(xiàn)的注解,代碼簡(jiǎn)單隨拿隨用
使用方式
1.創(chuàng)建一個(gè)注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EasyLock { long waitTime() default 1; long leaseTime() default 3; }
2. 創(chuàng)建一個(gè)AOP切面類(lèi)(異??梢宰远x,這里我用的Cicada)
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; 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.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.stereotype.Component; import vip.lspace.agent.common.annotation.EasyLock; import vip.lspace.agent.common.exception.LockException; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Component @Aspect @Slf4j public class LockAop { @Resource private LockException lockException; @Resource RedissonClient redissonClient; private static final String redisLockKeyParamName = "redisLockKey"; @Pointcut("@annotation(vip.lspace.agent.common.annotation.EasyLock)") public void lockPointcut() { } @Around("lockPointcut()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) { log.info("EasyLock locking"); MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature(); EasyLock easyLock = methodSignature.getMethod().getAnnotation(EasyLock.class); Object[] args = proceedingJoinPoint.getArgs(); String[] parameterNames = methodSignature.getParameterNames(); String redisLockKey = ""; for (int i = 0; i < parameterNames.length; i++) { if (parameterNames[i].equals(redisLockKeyParamName)) { redisLockKey = (String) args[i]; } } if (StringUtils.isBlank(redisLockKey)) { throw lockException.lockKeyNotExist(); } RLock lock = redissonClient.getLock(redisLockKey); try { if (!lock.tryLock(easyLock.waitTime(), easyLock.leaseTime(), TimeUnit.SECONDS)) { throw lockException.getLockTimeOut(redisLockKey); } return proceedingJoinPoint.proceed(); } catch (Throwable e) { throw new RuntimeException(e); } finally { if (lock.isLocked() && lock.isHeldByCurrentThread()) { lock.unlock(); log.info("當(dāng)前線程{},釋放鎖:{}", Thread.currentThread().getId(), redisLockKey); } } }
@CicadaBean(namespace = "lock") public interface LockException { @ExceptionInfo(errCode = 13001, errMessage = "沒(méi)找到分布式鎖key") CicadaException lockKeyNotExist(); @ExceptionInfo(errCode = 13002, errMessage = "超時(shí)未獲取到鎖: %s") CicadaException getLockTimeOut(String key); }
3.使用方式
注意點(diǎn):
必須包含名為redisLockKey的參數(shù),作為redis的key
@Override @EasyLock(waitTime = 1, leaseTime = 3 ) @Transactional(rollbackFor = Exception.class) public void concurrentTest(String redisLockKey) { PaymentBillBacktrack paymentBillBacktrack = paymentBillBacktrackService.getById(1L); String refundRequestNo = paymentBillBacktrack.getMerchantRefundRequestNo(); paymentBillBacktrack.setMerchantRefundRequestNo(String.valueOf(Integer.parseInt(refundRequestNo) + 1)); paymentBillBacktrackService.updateById(paymentBillBacktrack); }
第二種:@NiceLock
簡(jiǎn)介
大佬提供的公共組件,引包后可直接使用,使用簡(jiǎn)單
使用方式
1.引包
一定得引入1.1.6的,甚至最新版本,不然有問(wèn)題?。?!
<dependency> <groupId>com.suchtool</groupId> <artifactId>nicelock-spring-boot-starter</artifactId> <version>1.1.6</version> </dependency>
2.使用
@Override @Transactional @NiceLock( keys = {"#userId"}, acquireTimeout = 3000L, exception = NiceLockLockFailException.class, message = "服務(wù)已完成評(píng)價(jià),不能重復(fù)提交" ) public void test(String userId) { System.out.println("修改訂單: 用戶(hù)ID=" + userId); }
注解中傳入了exception參數(shù)后,報(bào)錯(cuò)會(huì)輸出message的內(nèi)容,更加直觀
測(cè)試方式
Apifox中的自動(dòng)化測(cè)試中可以配多個(gè)線程同時(shí)執(zhí)行接口,測(cè)試重復(fù)點(diǎn)擊是否生效
到此這篇關(guān)于Spring防止重復(fù)點(diǎn)擊的兩種實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Spring防止重復(fù)點(diǎn)擊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java存儲(chǔ)過(guò)程調(diào)用CallableStatement的方法
這篇文章主要介紹了Java存儲(chǔ)過(guò)程調(diào)用CallableStatement的方法,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下2020-11-11Java內(nèi)存模型之重排序的相關(guān)知識(shí)總結(jié)
重排序是指編譯器和處理器為了優(yōu)化性能而對(duì)指令序列進(jìn)行重新排序的一種手段,文中詳細(xì)介紹了Java重排序的相關(guān)知識(shí),需要的朋友可以參考下2021-06-06Kotlin 內(nèi)聯(lián)函數(shù)詳解及實(shí)例
這篇文章主要介紹了Kotlin 內(nèi)聯(lián)函數(shù)詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06基于Java中Math類(lèi)的常用函數(shù)總結(jié)
下面小編就為大家?guī)?lái)一篇基于Java中Math類(lèi)的常用函數(shù)總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09