程序员蜗牛
1 year ago
3 changed files with 154 additions and 0 deletions
@ -0,0 +1,22 @@ |
|||||
|
package com.woniu.demo.config; |
||||
|
|
||||
|
import org.redisson.Redisson; |
||||
|
import org.redisson.api.RedissonClient; |
||||
|
import org.redisson.config.Config; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
|
||||
|
@Configuration |
||||
|
public class RedissonConfig { |
||||
|
|
||||
|
@Bean |
||||
|
public RedissonClient redissonClient() { |
||||
|
Config config = new Config(); |
||||
|
// 这里假设你使用单节点的Redis服务器
|
||||
|
config.useSingleServer() |
||||
|
// 使用与Spring Data Redis相同的地址
|
||||
|
.setAddress("redis://127.0.0.1:6379"); |
||||
|
return Redisson.create(config); |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,126 @@ |
|||||
|
package com.woniu.demo.redisson; |
||||
|
|
||||
|
import com.woniu.demo.annotation.RequestKeyParam; |
||||
|
import com.woniu.demo.annotation.RequestLock; |
||||
|
import com.woniu.demo.exception.biz.BizException; |
||||
|
import com.woniu.demo.model.response.ResponseCodeEnum; |
||||
|
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.redisson.api.RLock; |
||||
|
import org.redisson.api.RedissonClient; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.core.annotation.Order; |
||||
|
import org.springframework.util.ReflectionUtils; |
||||
|
import org.springframework.util.StringUtils; |
||||
|
|
||||
|
import java.lang.annotation.Annotation; |
||||
|
import java.lang.reflect.Field; |
||||
|
import java.lang.reflect.Method; |
||||
|
import java.lang.reflect.Parameter; |
||||
|
|
||||
|
/** |
||||
|
* 接口优化,如何接口防抖(防重复提交)? |
||||
|
* 分布式锁实现方案! |
||||
|
* @description |
||||
|
*/ |
||||
|
@Aspect |
||||
|
@Configuration |
||||
|
@Order(2) |
||||
|
public class RedissonRequestLockAspect { |
||||
|
|
||||
|
private RedissonClient redissonClient; |
||||
|
|
||||
|
@Autowired |
||||
|
public RedissonRequestLockAspect(RedissonClient redissonClient) { |
||||
|
this.redissonClient = redissonClient; |
||||
|
} |
||||
|
|
||||
|
@Around("execution(public * * (..)) && @annotation(com.summo.demo.config.requestlock.RequestLock)") |
||||
|
public Object interceptor(ProceedingJoinPoint joinPoint) { |
||||
|
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); |
||||
|
Method method = methodSignature.getMethod(); |
||||
|
RequestLock requestLock = method.getAnnotation(RequestLock.class); |
||||
|
if (StringUtils.isEmpty(requestLock.prefix())) { |
||||
|
throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "重复提交前缀不能为空"); |
||||
|
} |
||||
|
//获取自定义key
|
||||
|
final String lockKey = getLockKey(joinPoint); |
||||
|
// 使用Redisson分布式锁的方式判断是否重复提交
|
||||
|
RLock lock = redissonClient.getLock(lockKey); |
||||
|
boolean isLocked = false; |
||||
|
try { |
||||
|
//尝试抢占锁
|
||||
|
isLocked = lock.tryLock(); |
||||
|
//没有拿到锁说明已经有了请求了
|
||||
|
if (!isLocked) { |
||||
|
throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,请稍后重试"); |
||||
|
} |
||||
|
//拿到锁后设置过期时间
|
||||
|
lock.lock(requestLock.expire(), requestLock.timeUnit()); |
||||
|
try { |
||||
|
return joinPoint.proceed(); |
||||
|
} catch (Throwable throwable) { |
||||
|
throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "系统异常"); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
throw new BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,请稍后重试"); |
||||
|
} finally { |
||||
|
//释放锁
|
||||
|
if (isLocked && lock.isHeldByCurrentThread()) { |
||||
|
lock.unlock(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public String getLockKey(ProceedingJoinPoint joinPoint) { |
||||
|
//获取连接点的方法签名对象
|
||||
|
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); |
||||
|
//Method对象
|
||||
|
Method method = methodSignature.getMethod(); |
||||
|
//获取Method对象上的注解对象
|
||||
|
RequestLock requestLock = method.getAnnotation(RequestLock.class); |
||||
|
//获取方法参数
|
||||
|
final Object[] args = joinPoint.getArgs(); |
||||
|
//获取Method对象上所有的注解
|
||||
|
final Parameter[] parameters = method.getParameters(); |
||||
|
StringBuilder sb = new StringBuilder(); |
||||
|
for (int i = 0; i < parameters.length; i++) { |
||||
|
final RequestKeyParam keyParam = parameters[i].getAnnotation(RequestKeyParam.class); |
||||
|
//如果属性不是RequestKeyParam注解,则不处理
|
||||
|
if (keyParam == null) { |
||||
|
continue; |
||||
|
} |
||||
|
//如果属性是RequestKeyParam注解,则拼接 连接符 "& + RequestKeyParam"
|
||||
|
sb.append(requestLock.delimiter()).append(args[i]); |
||||
|
} |
||||
|
//如果方法上没有加RequestKeyParam注解
|
||||
|
if (StringUtils.isEmpty(sb.toString())) { |
||||
|
//获取方法上的多个注解(为什么是两层数组:因为第二层数组是只有一个元素的数组)
|
||||
|
final Annotation[][] parameterAnnotations = method.getParameterAnnotations(); |
||||
|
//循环注解
|
||||
|
for (int i = 0; i < parameterAnnotations.length; i++) { |
||||
|
final Object object = args[i]; |
||||
|
//获取注解类中所有的属性字段
|
||||
|
final Field[] fields = object.getClass().getDeclaredFields(); |
||||
|
for (Field field : fields) { |
||||
|
//判断字段上是否有RequestKeyParam注解
|
||||
|
final RequestKeyParam annotation = field.getAnnotation(RequestKeyParam.class); |
||||
|
//如果没有,跳过
|
||||
|
if (annotation == null) { |
||||
|
continue; |
||||
|
} |
||||
|
//如果有,设置Accessible为true(为true时可以使用反射访问私有变量,否则不能访问私有变量)
|
||||
|
field.setAccessible(true); |
||||
|
//如果属性是RequestKeyParam注解,则拼接 连接符" & + RequestKeyParam"
|
||||
|
sb.append(requestLock.delimiter()).append(ReflectionUtils.getField(field, object)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//返回指定前缀的key
|
||||
|
return requestLock.prefix() + sb; |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue