基于redission+aop自定义分布式锁注解
由于我们项目中进场会用到分布式锁,所以我们定义一个注解来简化代码开发
1、常量类
/**
* 分布式锁常量
*/
public class DistributeLockConstant {
public static final String NONE_KEY = "NONE";
public static final String DEFAULT_OWNER = "DEFAULT";
public static final int DEFAULT_EXPIRE_TIME = -1;
public static final int DEFAULT_WAIT_TIME = Integer.MAX_VALUE;
}
2、注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
/**
* 锁的场景
*
* @return
*/
public String scene();
/**
* 加锁的key,优先取key(),如果没有,则取keyExpression()
*
* @return
*/
public String key() default DistributeLockConstant.NONE_KEY;
/**
* SPEL表达式:
* <pre>
* #id
* #insertResult.id
* </pre>
*
* @return
*/
public String keyExpression() default DistributeLockConstant.NONE_KEY;
/**
* 超时时间,毫秒
* 默认情况下不设置超时时间,会自动续期
*
* @return
*/
public int expireTime() default DistributeLockConstant.DEFAULT_EXPIRE_TIME;
/**
* 加锁等待时长,毫秒
* 默认情况下不设置等待时长,会一直等待直到获取到锁
* @return
*/
public int waitTime() default DistributeLockConstant.DEFAULT_WAIT_TIME;
}
3、使用方法
@DistributeLock(keyExpression = "#telephone", scene = "USER_REGISTER")
4、核心AOP代码
@Order(Integer.MIN_VALUE)主要作用是如果多个AOP在同一个方法上,那么需要保证分布式锁的优先级最高,即@Order的value值最小
例如这种情况
@Transactional(rollbackFor = Exception.class)
@DistributeLock
如果不设置执行顺序的话,默认的顺序为
- 创建事务
- 加锁
- 解锁
- 提交事务
如果在3,4步骤中,分布式锁已经释放锁了,但是事务还没提交,那么其他线程并发的时候又可以拿到锁对数据进行操作,会有数据安全问题。
/**
* 分布式锁切面
*/
@Aspect
@Component
@Order(Integer.MIN_VALUE)
public class DistributeLockAspect {
private RedissonClient redissonClient;
public DistributeLockAspect(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
private static final Logger LOG = LoggerFactory.getLogger(DistributeLockAspect.class);
@Around("@annotation(cn.com.lock.DistributeLock)")
public Object process(ProceedingJoinPoint pjp) throws Exception {
Object response = null;
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);
String key = distributeLock.key();
if (DistributeLockConstant.NONE_KEY.equals(key)) {
if (DistributeLockConstant.NONE_KEY.equals(distributeLock.keyExpression())) {
throw new DistributeLockException("no lock key found...");
}
// SPEL表达式解析
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(distributeLock.keyExpression());
EvaluationContext context = new StandardEvaluationContext();
// 获取参数值
Object[] args = pjp.getArgs();
// 获取运行时参数的名称
StandardReflectionParameterNameDiscoverer discoverer
= new StandardReflectionParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
// 将参数绑定到context中
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
}
// 解析表达式,获取结果
key = String.valueOf(expression.getValue(context));
}
String scene = distributeLock.scene();
String lockKey = scene + "#" + key;
int expireTime = distributeLock.expireTime();
int waitTime = distributeLock.waitTime();
RLock rLock = redissonClient.getLock(lockKey);
boolean lockResult = false;
if (waitTime == DistributeLockConstant.DEFAULT_WAIT_TIME) {
if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {
LOG.info(String.format("lock for key : %s", lockKey));
rLock.lock();
} else {
LOG.info(String.format("lock for key : %s , expire : %s", lockKey, expireTime));
rLock.lock(expireTime, TimeUnit.MILLISECONDS);
}
lockResult = true;
} else {
//设置了等待时间的情况,tryLock尝试获取锁
if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {
LOG.info(String.format("try lock for key : %s , wait : %s", lockKey, waitTime));
lockResult = rLock.tryLock(waitTime, TimeUnit.MILLISECONDS);
} else {
//设置了等待时间和超时时间的情况,tryLock尝试获取锁,并设置超时时间和等待时间
LOG.info(String.format("try lock for key : %s , expire : %s , wait : %s", lockKey, expireTime, waitTime));
lockResult = rLock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);
}
}
if (!lockResult) {
LOG.warn(String.format("lock failed for key : %s , expire : %s", lockKey, expireTime));
throw new DistributeLockException("acquire lock failed... key : " + lockKey);
}
try {
LOG.info(String.format("lock success for key : %s , expire : %s", lockKey, expireTime));
response = pjp.proceed();
} catch (Throwable e) {
throw new Exception(e);
} finally {
rLock.unlock();
LOG.info(String.format("unlock for key : %s , expire : %s", lockKey, expireTime));
}
return response;
}
}
5、异常类
/**
* 分布式锁异常类
*/
public class DistributeLockException extends RuntimeException {
public DistributeLockException() {
}
public DistributeLockException(String message) {
super(message);
}
public DistributeLockException(String message, Throwable cause) {
super(message, cause);
}
public DistributeLockException(Throwable cause) {
super(cause);
}
public DistributeLockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}