基于redisson框架,以注解的形式实现springboot项目的分布式锁
使用方式
@RedissonLock(lockName = CacheConst.BATCH_MESSAGE_PUSH_LOCK, key = "#userId", expire = 10, autoPrefix = 2)
package com.meta.service.api.common.annotion;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 使用redis构建分布式锁
* @RedisLock(lockName = "deductionStock", key = "#deductionStockDto.purchaseId")
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedissonLock {
//redis锁的名称
String lockName() default "";
//redis锁 key支持spring spel表达式
String key() default "";
//锁前缀 1:默认加前缀 2:key()为准
int autoPrefix() default 1;
//锁过期时间 默认时间为秒单位
int expire() default 120;
//超时时间单位为秒
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
package com.meta.service.api.common.aspect;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.meta.service.api.common.annotion.RedissonLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.expression.MethodBasedEvaluationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* redis分布式锁切面
* @author smy
*/
@Aspect
@Component
@Slf4j
@Order(1)
public class RedisLockAspect {
@Autowired
private RedissonClient redissonClient;
//定义分布式锁key的前缀
private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";
@Around("@annotation(redisLock)")
public Object around(ProceedingJoinPoint joinPoint, RedissonLock redisLock) throws Throwable {
//获取redisLock key值这里使用spel作为key
String spel = redisLock.key();
String lockName = redisLock.lockName();
int autoPrefix = redisLock.autoPrefix();
//通过key获取对应的锁
String key = getRedisKeyBySpel(joinPoint, lockName, spel, autoPrefix);
RLock rLock = redissonClient.getLock(key);
rLock.lock(redisLock.expire(), redisLock.timeUnit());
log.info("获取redis锁key:{}", key);
Object result = null;
try {
//执行目标方法
result = joinPoint.proceed();
} finally {
rLock.unlock();
log.info("释放redis锁key:{}", key);
}
return result;
}
/**
* 通过spel表达式转为字符串格式
*
* @param joinPoint
* @param lockName
* @param spel
* @param autoPrefix
* @return
*/
private String getRedisKeyBySpel(ProceedingJoinPoint joinPoint, String lockName, String spel, int autoPrefix) {
//获取被增强的方法相关信息
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
//获取对应的执行方法
Method targetMethod = methodSignature.getMethod();
Object target = joinPoint.getTarget();
Object[] args = joinPoint.getArgs();
if (autoPrefix == 1) {
return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + parse(target, spel, targetMethod, args);
} else {
String parse = parse(target, spel, targetMethod, args);
if (StringUtils.isBlank(parse)) {
return lockName;
}
return lockName + StrUtil.COLON + parse;
}
}
/**
* 支持 #p0 参数索引的表达式解析
* @param rootObject 根对象,method 所在的对象
* @param spel 表达式
* @param method ,目标方法
* @param args 方法入参
* @return 解析后的字符串
*/
public String parse(Object rootObject, String spel, Method method, Object[] args) {
if (StrUtil.isBlank(spel)) {
return StrUtil.EMPTY;
}
//获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u =
new LocalVariableTableParameterNameDiscoverer();
String[] paraNameArr = u.getParameterNames(method);
if (ArrayUtil.isEmpty(paraNameArr)) {
return spel;
}
//使用SPEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
//SPEL上下文
StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, u);
//把方法参数放入SPEL上下文中
for (int i = 0; i < paraNameArr.length; i++) {
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(spel).getValue(context, String.class);
}
}
本文介绍如何利用Redisson框架,在SpringBoot应用中通过注解方式实现分布式锁,确保多线程环境下的资源安全。
1802

被折叠的 条评论
为什么被折叠?



