欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > spring防止重复点击,两种注解实现(AOP)

spring防止重复点击,两种注解实现(AOP)

2025/5/3 19:17:59 来源:https://blog.csdn.net/m0_59465624/article/details/144932278  浏览:    关键词:spring防止重复点击,两种注解实现(AOP)

第一种:@EasyLock

简介

为了简化可复用注解,自己实现的注解,代码简单随拿随用

使用方式

1.创建一个注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EasyLock {long waitTime() default 1;long leaseTime() default 3;
}

 2. 创建一个AOP切面类(异常可以自定义,这里我用的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 {@Resourceprivate LockException lockException;@ResourceRedissonClient 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("当前线程{},释放锁:{}", Thread.currentThread().getId(), redisLockKey);}}}
@CicadaBean(namespace = "lock")
public interface LockException {@ExceptionInfo(errCode = 13001, errMessage = "没找到分布式锁key")CicadaException lockKeyNotExist();@ExceptionInfo(errCode = 13002, errMessage = "超时未获取到锁: %s")CicadaException getLockTimeOut(String key);
}

3.使用方式

 注意点:

必须包含名为redisLockKey的参数,作为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

简介

大佬提供的公共组件,引包后可直接使用,使用简单,细节代码可看nicelock: nicelock:一个注解,即可使用Java的分布式锁。(基于Redisson)

使用方式

1.引包

一定得引入1.1.6的,甚至最新版本,不然有问题!!!

<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 = "服务已完成评价,不能重复提交")public void test(String userId) {System.out.println("修改订单: 用户ID=" + userId);}

注解中传入了exception参数后,报错会输出message的内容,更加直观 

测试方式

Apifox中的自动化测试中可以配多个线程同时执行接口,测试重复点击是否生效

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词