项目用 Redisson 分布式锁,但是每个地方的代码除了业务代码,其他都差不多一样的,如果要修改的话,就要修改很多,不只修改一个项目,很麻烦的。
简单封装:
- 自定义注解
- 获取锁过程接口
自定义注解
需要在加锁的方法上面贴一个注解,利用 Spring AOP 动态代理的特性进行处理,配置参数值即可。
1.加锁注解
代码语言:javascript复制package com.tansci.common.redisson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName:Lock.java
* @ClassPath:com.tansci.common.redisson.annotation.Lock.java
* @Description: 加锁注解
*
* <p>
* 示例:@Lock(lockKey = "key", businessCode = "A0001", waitTime = 0, leaseTime = 30000)
* </p>
* @Author:tanyp
* @Date:2022/8/30 15:22
**/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Lock {
/**
* 锁的key
*/
String lockKey() default "";
/**
* 业务code
*/
String businessCode() default "";
/**
* 获取锁的等待时间(毫秒)
*/
int waitTime() default 0;
/**
* 释放的时间(毫秒)
*/
int leaseTime() default 0;
}
2.分布式锁切面
代码语言:javascript复制package com.tansci.common.redisson.aop;
import com.tansci.common.redisson.annotation.Lock;
import com.tansci.common.redisson.exception.LockException;
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.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:LockAspect.java
* @ClassPath:com.tansci.common.redisson.aop.LockAspect.java
* @Description: 分布式锁切面
* @Author:tanyp
* @Date:2022/8/31 9:07
**/
@Aspect
@Component
public class LockAspect {
@Autowired
private RedissonClient redissonClient;
@Around("@annotation(com.tansci.common.redisson.annotation.Lock)")
public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Lock lock = method.getAnnotation(Lock.class);
String lockKey = getLockKeyName(lock, joinPoint);
RLock rLock = redissonClient.getLock(lockKey);
boolean flag = getLock(rLock, lock);
try {
if (flag) {
return joinPoint.proceed();
} else {
throw new LockException("网络繁忙,请稍后再试");
}
} catch (Exception e) {
throw new LockException("服务器繁忙,请稍后再试");
} finally {
if (flag) {
if (rLock.isLocked()) {
rLock.unlock();
}
}
}
}
private String getLockKeyName(Lock lock, ProceedingJoinPoint joinPoint) {
String lockKeyPrefix = lock.lockKey();
String businessCode = lock.businessCode();
if (Objects.nonNull(lockKeyPrefix) && Objects.nonNull(businessCode)) {
return lockKeyPrefix ":" businessCode;
} else if (Objects.nonNull(lockKeyPrefix)) {
return lockKeyPrefix;
} else if (Objects.nonNull(businessCode)) {
return businessCode;
} else {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
return className "." methodName;
}
}
private boolean getLock(RLock rLock, Lock lock) throws InterruptedException {
int waitTime = lock.waitTime();
int leaseTime = lock.leaseTime();
boolean flag = false;
if (waitTime > 0 && leaseTime > 0) {
flag = rLock.tryLock(waitTime, leaseTime, TimeUnit.MICROSECONDS);
} else if (leaseTime > 0) {
flag = rLock.tryLock(0, leaseTime, TimeUnit.MILLISECONDS);
} else if (waitTime > 0) {
flag = rLock.tryLock(waitTime, TimeUnit.MILLISECONDS);
} else {
flag = rLock.tryLock();
}
return flag;
}
}
3.分布式锁异常处理
代码语言:javascript复制package com.tansci.common.redisson.exception;
/**
* @ClassName:LockException.java
* @ClassPath:com.tansci.common.redisson.exception.LockException.java
* @Description: 分布式锁异常处理
* @Author:tanyp
* @Date:2022/8/31 9:08
**/
public class LockException extends RuntimeException {
/**
* 异常码
*/
private int code = 500;
/**
* 异常描述
*/
private String message;
public LockException(Integer code, String message, Throwable e) {
super(message, e);
this.code = code;
this.message = message;
}
public LockException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
public LockException(String message) {
super(message);
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return code ": " message;
}
}
使用示例
代码语言:javascript复制@Lock(lockKeyPrefix = "testKey", businessCode = "A0001", waitTime = 0, leaseTime = 30000)
@GetMapping("/testLock")
public String testLock() throws Exception {
return "分布式锁注解测试....";
}
获取锁过程接口
直接调用工具类方法,只关心业务逻辑处理部分,可以拓展成多个接口实现不同的业务锁。
代码语言:javascript复制package com.tansci.common.redisson.handler;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* @ClassName:RedissonLockHandler.java
* @ClassPath:com.tansci.common.redisson.handler.RedissonLockHandler.java
* @Description:redisson分布式锁
* @Author:tanyp
* @Date:2022/8/31 9:12
**/
@Service
@Transactional
public class RedissonLockHandler {
@Autowired
private RedissonClient redissonClient;
/**
* @MonthName:lock
* @Description: 获取锁
* @Author:tanyp
* @Date:2022/8/31 9:20
* @Param: [key, waitTime, leaseTime, success, fail]
* @return:T
**/
public <T> T lock(String key, int waitTime, int leaseTime, Supplier<T> success, Supplier<T> fail) throws Exception {
/**
* @Autowired
* private RedissonLockHandler redissonLockHandler;
*
* return redissonLockHandler.lock("key",0,30000,()->{
* //获取锁成功执行逻辑
* return "获取锁成功";
* },()->{
* //获取锁失败执行逻辑
* return "获取锁失败";
* });
*/
RLock lock = redissonClient.getLock(key);
boolean flag = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
try {
if (flag) {
return success.get();
} else {
return fail.get();
}
} finally {
if (flag) {
if (lock.isLocked()) {
lock.unlock();
}
}
}
}
}
使用示例
代码语言:javascript复制@Autowired
private RedissonLockHandler redissonLockHandler;
@GetMapping("/testLock")
public String testLock() throws Exception {
return redissonLockHandler.lock("testKey",0,30000,()->{
// 获取锁成功执行逻辑
return "获取锁成功";
},()->{
// 获取锁失败执行逻辑
return "获取锁失败";
});
}
区别
- 第一种适用于方法级别的,代码更优雅舒服一些。
- 第二种通用,整个方法全部代码块和部分代码块都可,业务拓展性更高。