一、项目中引入Redisson
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.17.4</version>
</dependency>
二、在项目中加上redis链接信息
redis:
host: ip
port: 端口
database: 0
password:
三、本地封装Redisson分布式锁
1.定义RedisLock注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
//锁名称
String lockName();
//参数 格式:#参数名, 对象用#对象名.字段名
String lockParam() default "";
// 锁的持续时间,默认为30秒
//不需要担心运行的进程中锁过早释放,Redisson 的看门狗机制会自动续期锁的过期时间,防止长时间运行的进程中锁过早释放。这一机制默认每 10 秒检测一次,如果锁还被持有,则会将锁的过期时间重新设置为 30 秒。
long leaseTime() default 30;
// 时间单位,默认为秒
TimeUnit timeUnit() default TimeUnit.SECONDS;
/***
}
2.创建RedisLock的切面RedissonLockAspect
@Aspect
@Component
public class RedissonLockAspect {
@Autowired
private RedissonClient redissonClient;
@Around("@annotation(redisLock)")
public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
String lockName = redisLock.lockName();
if (StrUtil.isBlank(lockName)){
throw new ServiceException("锁名称不能为空");
}
String lockParam = redisLock.lockParam();
if (StrUtil.isNotBlank(lockParam)){
String paramStr = getParam(joinPoint, lockParam);
lockName = StrUtil.format("{}:{}", lockName, paramStr);
}
RLock lock = redissonClient.getLock(lockName);
Object result = null;
try {
lock.lock(redisLock.leaseTime(), redisLock.timeUnit());
//执行方法
result = joinPoint.proceed();
} finally {
if (lock != null && lock.isHeldByCurrentThread()){
lock.unlock();
}
}
return result;
}
/**
* 获取参数
* @param point
* @param lockParam
* @return
*/
private String getParam(ProceedingJoinPoint point, String lockParam) {
// 获取方法签名
MethodSignature signature = (MethodSignature) point.getSignature();
// 获取方法参数值
Object[] args = point.getArgs();
// 获取参数名称
String[] parameterNames = signature.getParameterNames();
// 创建参数映射,将参数名称映射到相应的值
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
// 解析lockParam中的SpEL表达式
ExpressionParser parser = new SpelExpressionParser();
String paramStr = parser.parseExpression(lockParam).getValue(context, String.class);
return paramStr;
}
}
3.创建配置类,加载redis配置信息
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.database}")
private int database;
/**
*
* @return
* @throws IOException
*/
@Bean
public RedissonClient redisson() throws IOException {
Config config = new Config();
config.useSingleServer().setAddress("redis://"+redisHost+":"+port).setPassword(password).setDatabase(database);
return Redisson.create(config);
}
}
四、如何使用注解
@RedisLock(lockName = "锁名称", lockParam = "参数")
说明:注解直接加载方法上即可
lockName :这是基础的锁名称,为必填项。它定义了锁的基本标识。当不需要基于方法的参数来区分锁时,只使用 lockName 就足够了。
lockParam :参数可选,格式:#参数名, 对象用#对象名.字段名。这个参数用于进一步细化锁的名称,使锁可以基于方法的动态参数值来具体化。这是通过解析方法参数来实现的,允许锁不仅基于静态名称(lockName),而且还基于方法调用时的参数值。例如,如果一个方法处理多个用户的数据,并且你想为每个用户单独上锁以避免相互影响,可以将用户ID作为 lockParam。