使用Spring的表达式解析器+Redis实现分布式锁
怎么配置使用Redis就不在这里说明了!!!
AOP依赖
<!--引用AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.0 定义分布式锁注解
/**
* @Author: ZhiHao
* @Date: 2022/7/28 20:19
* @Description: 分布式锁注解
* @Versions 1.0
**/
@Documented
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {
/**
* 锁前缀
*
* @return
*/
String lockKeyPrefix() default "";
/**
* 为空默认以用户维度加锁
* 锁Key参数解析表达式 (自定义实体是 User user => #user.id 、
* | Map<String, String> params => #params['phone']
* | string、int params => #params
* | 多个参数 => 'id_'+#user.id+'age_'+#user.age
*
* @return
*/
String lockKeyValue();
/**
* 锁住时间 (秒)
* @return
*/
double lockTime() default 1.5;
/**
* 加锁失败抛异常的提示
* @return
*/
LockFailTips lockFailTips() default LockFailTips.DEFAULT_VALUE;
/**
* 添加用户 masterId_12345 标识
* @return
*/
boolean addLockKeyUserFlag() default false;
}
2.0 失败抛异常提示枚举
public enum LockFailTips {
ORDER_SUBMIT(()->{throw new RuntimeException("订单提交点击太快了!");},"订单信息提交"),
DEFAULT_VALUE(()->{throw new RuntimeException("加分布式锁失败!");},"默认提示"),
;
private Supplier<?> supplier;
private String describe;
LockFailTips(Supplier<?> supplier, String describe) {
this.supplier = supplier;
this.describe = describe;
}
public Supplier<?> getSupplier() {
return supplier;
}
}
3.0 注解式AOP实现
/**
* @Author: ZhiHao
* @Date: 2022/7/28 20:07
* @Description:
* @Versions 1.0
**/
@Aspect
@Service
@Slf4j
public class RedisDistributedLock implements Ordered {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private HttpServletRequest request;
private static final String PROJECT_NAME = "order-api:";
private static final String LOCK_SCRIPT = "if redis.call('setnx', KEYS[1], ARGV[1] ) == 1 then return redis.call('pexpire', KEYS[1], tonumber(ARGV[2])) else return 0 end";
private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
private static final DefaultRedisScript<Long> lock_script;
private static final DefaultRedisScript<Long> release_lock_script;
// 表达式解析器
private static final ExpressionParser expressionParser = new SpelExpressionParser();
// 类方法参数命名获取, 底层是通过读取文件解析获取到的
private static final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
= new LocalVariableTableParameterNameDiscoverer();
static {
lock_script = new DefaultRedisScript<>();
lock_script.setScriptText(LOCK_SCRIPT);
lock_script.setResultType(Long.class);
release_lock_script = new DefaultRedisScript<>();
release_lock_script.setScriptText(RELEASE_LOCK_SCRIPT);
release_lock_script.setResultType(Long.class);
}
@Pointcut("@annotation(com.zhihao.annotation.DistributedLock)")
private void expression() {
}
/**
* 使用环谣通知
* @Before("expression()") => @Before("@annotation(distributedLock)") 可以直接方法参数定义使用注解
* @param joinPoint
* @param distributedLock
* @author: ZhiHao
* @date: 2022/7/28
* @return
*/
@Around("@annotation(distributedLock)")
public Object lock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
// 锁的值
String value = UUID.randomUUID().toString();
// 获取锁的key
String lockKey = this.getLockKey(joinPoint,distributedLock);
if (log.isDebugEnabled()){
log.debug("distributedLock===requestUrl:{}, lockKey:{}, requestId:{}",request.getServletPath(),lockKey,requestId);
}
try {
// 加锁
Boolean lock = this.distributedLock(lockKey,value, distributedLock);
if (!lock){
// 加锁失败抛出异常
distributedLock.lockFailTips().getSupplier().get();
}
// 执行被切面方法
return joinPoint.proceed(joinPoint.getArgs());
} finally {
// 解锁
redisTemplate.execute(release_lock_script, Collections.singletonList(lockKey), value);
}
}
/**
* 加锁
*
* @param lockKey
* @param value
* @param distributedLock
* @return java.lang.Boolean true=成功
* @author: ZhiHao
* @date: 2022/7/28
*/
private Boolean distributedLock(String lockKey, String value, DistributedLock distributedLock) {
double lockTime = distributedLock.lockTime() * 1000;
Long result = redisTemplate.execute(lock_script, Collections.singletonList(lockKey), value, String.valueOf(lockTime));
return Optional.ofNullable(result).orElse(0L).equals(1L);
}
/**
* 获取Key
*
* @param joinPoint
* @param distributedLock
* @return java.lang.String
* @author: ZhiHao
* @date: 2022/7/28
*/
private String getLockKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
StringBuilder keyBuilder = new StringBuilder(PROJECT_NAME);
// 获取注解数据
String keyPrefix = distributedLock.lockKeyPrefix();
String keyValue = distributedLock.lockKeyValue();
keyBuilder.append(keyPrefix);
// 获取方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取参数
Object[] args = joinPoint.getArgs();
// 获取方法
Method targetMethod = methodSignature.getMethod();
// 获取方法参数名称
String[] parameterNames = parameterNameDiscoverer.getParameterNames(targetMethod);
// 如果表达式为空, 或者表达式不为空, 参数为空, 使用方法名称 作为Key
Integer length = Optional.ofNullable(args).map(el -> el.length).orElse(0);
if (StrUtil.isBlank(keyValue) || (StrUtil.isNotBlank(keyValue) && length <= 0)) {
return keyBuilder.append(keyValue).append(targetMethod.getName()).toString().trim();
}
// 封装表达式变量和参数
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
for (int i = 0; i < args.length; i++) {
evaluationContext.setVariable(parameterNames[i], args[i]);
}
// 解析表达式
Expression expression = expressionParser.parseExpression(keyValue);
String value = expression.getValue(evaluationContext, String.class);
// 拼接最终key返回
return keyBuilder.append(value).toString();
}
// AOP优先级最高
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE+1;
}
}
4.0 使用
@PostMapping("/post")
// 取user里面的age属性setVariable(user, user)
@DistributedLock(lockKeyPrefix = "order_submit:",lockKeyValue = "#user.age",
lockFailTips = LockFailTips.ORDER_SUBMIT,LockTime = 5.5)
public User post(@RequestBody @Valid User user) {
System.out.println(user);
return user;
}
//-------------------------
@GetMapping("/gets")
// 取name属性setVariable(name, args[i])
@DistributedLock(lockKeyPrefix = "order_submitsss:",lockKeyValue = "#name",
lockFailTips = LockFailTips.ORDER_SUBMIT,LockTime = 5.5)
public User gets(String name) {
}
//-------------多个参数Map--------------
@DistributedLock(lockKeyPrefix = "superTeamManage_lockKey:memberInvite-", addLockKeyUserFlag = true,
lockKeyValue = "'phone_'+#params['phone']+'age_'+#params['age']")
@PostMapping("/memberInvite")
public BaseDialog memberInvite(@RequestBody @NotEmpty(message = "参数不能为空!") Map<String, String> params) {
return superTeamManageService.memberInvite(params.get("phone"));
}
//-------------多个参数自定义实体--------------
@DistributedLock(lockKeyPrefix = "order_submit:",lockKeyValue = "'age_'+#user.age+'name_'+#",
lockFailTips = LockFailTips.ORDER_SUBMIT,LockTime = 5.5)
public User post(@RequestBody @Valid User user) {
System.out.println(user);
return user;
}
如果获取的参数值为null, 表达式解析器解析出来的结果是:
phone_null
, 可用MVC搭配使用参数验证参数绑定, 先校验参数为空抛出异常!
StandardEvaluationContext 可以更换使用 MethodBasedEvaluationContext
private static final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
= new LocalVariableTableParameterNameDiscoverer();
// 封装表达式变量和参数
ExpressionRootObject rootObject = new ExpressionRootObject(joinPoint.getTarget(),args);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(rootObject ,targetMethod,args,parameterNameDiscoverer);
// 解析表达式
Expression expression = expressionParser.parseExpression(keyValue);
String value = expression.getValue(evaluationContext, String.class);
// -----------------------ExpressionRootObject--------------------------
@Data
public class ExpressionRootObject {
private final Object object;
private final Object[] args;
public ExpressionRootObject(Object object, Object[] args) {
this.object = object;
this.args = args;
}
}
优化: 缓存表达式Expression
自定义
ExpressionEvaluator
继承CachedExpressionEvaluator
@Component
public class ExpressionEvaluator extends CachedExpressionEvaluator {
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
public Object key(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext) {
// 调用父类实现的缓存表达式
return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext);
}
}
注入使用
@Autowired
private ExpressionEvaluator evaluator;
private String getLockKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
StringBuilder keyBuilder = new StringBuilder(PROJECT_NAME);
// 获取注解数据
String keyPrefix = distributedLock.lockKeyPrefix();
String keyValue = distributedLock.lockKeyValue();
keyBuilder.append(keyPrefix);
// 添加用户标识
if (distributedLock.addLockKeyUserFlag()) {
keyBuilder.append("masterId_").append(通过request获取).append("_");
}
// 获取方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取参数
Object[] args = joinPoint.getArgs();
// 获取方法
Method targetMethod = methodSignature.getMethod();
// 获取被切目标对象
Object target = joinPoint.getTarget();
// 如果表达式为空, 或者表达式不为空, 参数为空, 使用方法名称+用户标识, 作为Key
Integer length = Optional.ofNullable(args).map(el -> el.length).orElse(0);
if (StrUtil.isBlank(keyValue) || (StrUtil.isNotBlank(keyValue) && length <= 0)) {
return keyBuilder.append(keyValue).append(targetMethod.getName())
.append("masterId_").append(通过request获取).toString().trim();
}
// 封装表达式变量和参数
ExpressionRootObject rootObject = new ExpressionRootObject(target,args);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(rootObject ,targetMethod,args,parameterNameDiscoverer);
// 解析表达式
AnnotatedElementKey methodKey = new AnnotatedElementKey(targetMethod, target.getClass());
Object value = evaluator.key(keyValue, methodKey, evaluationContext);
// 拼接最终key返回
return keyBuilder.append(value).toString();
}
1