本地锁的基本使用
LocalLock 本地锁
实现 拒绝一个请求在规定时间内多次调用
1实现原理
当一个请求被提交,把请求方法的参数存到redis并设置过期时间(比如5秒),如果5秒内再次请求,redis中就可以查到信息,直接拒绝请求,否则正常通过
使用Spring AOP 对注解方法进行切面:
- 从redis中 通过获取注解传入的参数 来查询数据
- 如果不存在,就执行方法,并将参数为key放入redis,并设置过期时间为规定不可重复时间
- 如果存在,就抛出异常,停止执行方法
2实现代码
注解类:
/**
* @author : lzcer
* @date : 2020/11/9 8:45
* @description : 本地锁
* Target 注解目标 方法
* Retention 保留注解的策略
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LocalLock {
/*
定义属性key
*/
String key() default "";
}
切面类:
/**
* @author lzcer
*/
@Aspect
@Configuration
public class LocalMethodInterceptor {
private static final Cache<String, Object> CACHE = CacheBuilder.newBuilder()
//最大缓存个数
.maximumSize(100)
//缓存5秒过期
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
@Around("execution(public * *(..))&& @annotation(com.lzcer.care_free_final.framework.annotation.LocalLock)")
public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
LocalLock localLock = method.getAnnotation(LocalLock.class);
String key = getKey(localLock.key(), pjp.getArgs());
if (!StringUtils.isEmpty(key)) {
String cache = redisCache.getCacheObject(key);
if (cache != null && !"".equals(cache)) {
//有这个key 抛出不要重复提交
throw new UserException("user.repeat.submit", null);
}
redisCache.setCacheObject(key, key, localLock.time(), TIME_UNIT);
}
// 此处执行原方法 可能会有异常,切记万万不可捕获,否则将导致统一异常捕获失效
return pjp.proceed();
}
/**
* key的生成策略
* @param keyExpress 注解key值
* @param args 方法参数
* @return 生成的缓存的key
*/
private String getKey(String keyExpress, Object[] args) {
for (int i = 0; i < args.length; i++) {
keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());
}
return keyExpress;
}
}