package com.example.demo.lock.annotation;
import com.example.demo.lock.model.LockType;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DistributedLock {
/**
* 锁名
*
* @return 默认空值
*/
String name() default "";
/**
* 键值
* 支持 SpEL 表达式,如:#param.id
*
* @return 解析 SpEl 的值或原文
*/
String key() default "";
/**
* 尝试加锁,最多等待时间,时间单位以 unit 为准
* 默认值(-1):不等待
*
* @return 默认-1
*/
long waitTime() default -1;
/**
* 加锁以后多久自动解锁,时间单位以 unit 为准
* 默认值(-1):默认30秒,Redisson自动续期
* 其它值:按填写的值到期自动解锁,Redisson不会自动续期
*
* @return 默认-1
*/
long leaseTime() default -1;
/**
* 时间单位
*
* @return 默认秒
*/
TimeUnit unit() default TimeUnit.SECONDS;
/**
* 锁类型,
* 如:可重入锁、公平锁、读锁、写锁
*
* @return 默认可重入锁
*/
LockType lockType() default LockType.REENTRANT;
/**
* 是否联锁(多把锁合为一个锁操作),
* 可以在操作List类型锁使用,把List里的值合为一个锁。
*
* @return true-是联锁,false-非联锁
*/
boolean isMultiLock() default false;
/**
* 是否尝试加锁,
* true-尝试加锁,等待 waitTime 时间,无论成功还是失败都会返回;
* false-阻塞加锁,直到完成加锁才返回。
*
* @return true-尝试加锁,false-阻塞加锁
*/
boolean isTryLock() default true;
}
package com.example.demo.lock.aspect;
import com.alibaba.fastjson.JSON;
import com.example.demo.lock.annotation.DistributedLock;
import com.example.demo.lock.exception.TryLockFailException;
import com.example.demo.lock.provider.Lock;
import com.example.demo.lock.provider.LockFactory;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
@Order(0)
public class DistributedLockAspect {
@Autowired
private LockFactory lockFactory;
@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
Lock lock = lockFactory.getLock(joinPoint, distributedLock);
log.info("↓↓↓ LockInfo ↓↓↓{}{}", System.lineSeparator(), JSON.toJSONString(lock.getLockInfo(), true));
if (!lock.lock()) {
throw new TryLockFailException(String.format("加锁失败!加锁[%s]失败", lock.getLockInfo().getName()));
}
log.info("加锁成功!加锁[{}]成功", lock.getLockInfo().getName());
try {
return joinPoint.proceed();
} finally {
try {
lock.unlock();
log.info("解锁成功!解锁[{}]成功", lock.getLockInfo().getName());
} catch (Exception e) {
log.error("解锁失败!解锁[{}]失败", lock.getLockInfo().getName(), e);
}
}
}
}
package com.example.demo.lock.provider;
import com.example.demo.lock.annotation.DistributedLock;
import com.example.demo.lock.model.LockInfo;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@Component
public class LockKeyProvider {
private static final String KEY_PREFIX = "lock";
private static final String KEY_SEPARATOR = ":";
private static final String ERROR_NO_VALUE_MESSAGE = "表达式[%s]未获取到值";
private final ExpressionParser parser = new SpelExpressionParser();
private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
public Method getMethod(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
return methodSignature.getMethod();
}
private String concat(DistributedLock distributedLock, String value) {
Assert.hasText(value, String.format(ERROR_NO_VALUE_MESSAGE, distributedLock.key()));
return StringUtils.joinWith(KEY_SEPARATOR, KEY_PREFIX, distributedLock.name(), value);
}
public LockInfo getInfo(JoinPoint joinPoint, DistributedLock distributedLock) {
Object rootObject = joinPoint.getTarget();
Method method = getMethod(joinPoint);
Object[] args = joinPoint.getArgs();
EvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, discoverer);
Expression expression = parser.parseExpression(distributedLock.key());
LockInfo lockInfo = new LockInfo();
lockInfo.setName(concat(distributedLock, expression.getValue(context, String.class)));
if (distributedLock.isMultiLock()) {
List<?> values = expression.getValue(context, List.class);
Assert.notNull(values, String.format(ERROR_NO_VALUE_MESSAGE, distributedLock.key()));
List<String> keys = new ArrayList<>();
values.forEach(value -> keys.add(concat(distributedLock, value.toString())));
lockInfo.setMultiLockNames(keys);
}
return lockInfo;
}
}
package com.example.demo.lock.provider;
import com.example.demo.lock.model.LockInfo;
public interface Lock {
/**
* 加锁
*
* @return true-加锁成功;false-加锁失败
*/
boolean lock();
/**
* 解锁
*/
void unlock();
/**
* 获取锁信息
*
* @return 锁信息
*/
LockInfo getLockInfo();
}
package com.example.demo.lock.provider;
import com.example.demo.lock.model.LockInfo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.Assert;
@Slf4j
public abstract class AbstractLock implements Lock {
private final RedissonClient redissonClient;
private final LockInfo lockInfo;
private RLock rLock;
protected AbstractLock(RedissonClient redissonClient, LockInfo lockInfo) {
this.redissonClient = redissonClient;
this.lockInfo = lockInfo;
}
protected RedissonClient getRedissonClient() {
return redissonClient;
}
@Override
public LockInfo getLockInfo() {
return lockInfo;
}
/**
* 获取 Redisson Lock
*
* @return RLock
*/
protected abstract RLock getRedissonLock();
@Override
public boolean lock() {
try {
rLock = getRedissonLock();
if (getLockInfo().isTryLock()) {
return rLock.tryLock(getLockInfo().getWaitTime(), getLockInfo().getLeaseTime(), getLockInfo().getUnit());
} else {
rLock.lock(getLockInfo().getLeaseTime(), getLockInfo().getUnit());
return true;
}
} catch (Exception e) {
log.error("加锁失败", e);
return false;
}
}
@Override
public void unlock() {
Assert.notNull(rLock, "锁为空,未能执行解锁!");
rLock.unlock();
}
}
package com.example.demo.lock.provider.concrete;
import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
@Slf4j
public class ReentrantLock extends AbstractLock {
public ReentrantLock(RedissonClient redissonClient, LockInfo lockInfo) {
super(redissonClient, lockInfo);
}
@Override
protected RLock getRedissonLock() {
if (getLockInfo().isMultiLock()) {
RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
rLocks[i] = getRedissonClient().getLock(getLockInfo().getMultiLockNames().get(i));
}
return getRedissonClient().getMultiLock(rLocks);
} else {
return getRedissonClient().getLock(getLockInfo().getName());
}
}
}
package com.example.demo.lock.provider.concrete;
import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
@Slf4j
public class FairLock extends AbstractLock {
public FairLock(RedissonClient redissonClient, LockInfo lockInfo) {
super(redissonClient, lockInfo);
}
@Override
protected RLock getRedissonLock() {
if (getLockInfo().isMultiLock()) {
RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
rLocks[i] = getRedissonClient().getFairLock(getLockInfo().getMultiLockNames().get(i));
}
return getRedissonClient().getMultiLock(rLocks);
} else {
return getRedissonClient().getFairLock(getLockInfo().getName());
}
}
}
package com.example.demo.lock.provider.concrete;
import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
@Slf4j
public class ReadLock extends AbstractLock {
public ReadLock(RedissonClient redissonClient, LockInfo lockInfo) {
super(redissonClient, lockInfo);
}
@Override
protected RLock getRedissonLock() {
if (getLockInfo().isMultiLock()) {
RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
rLocks[i] = getRedissonClient().getReadWriteLock(getLockInfo().getMultiLockNames().get(i)).readLock();
}
return getRedissonClient().getMultiLock(rLocks);
} else {
return getRedissonClient().getReadWriteLock(getLockInfo().getName()).readLock();
}
}
}
package com.example.demo.lock.provider.concrete;
import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.AbstractLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
@Slf4j
public class WriteLock extends AbstractLock {
public WriteLock(RedissonClient redissonClient, LockInfo lockInfo) {
super(redissonClient, lockInfo);
}
@Override
protected RLock getRedissonLock() {
if (getLockInfo().isMultiLock()) {
RLock[] rLocks = new RLock[getLockInfo().getMultiLockNames().size()];
for (int i = 0; i < getLockInfo().getMultiLockNames().size(); i++) {
rLocks[i] = getRedissonClient().getReadWriteLock(getLockInfo().getMultiLockNames().get(i)).writeLock();
}
return getRedissonClient().getMultiLock(rLocks);
} else {
return getRedissonClient().getReadWriteLock(getLockInfo().getName()).writeLock();
}
}
}
package com.example.demo.lock.provider;
import com.example.demo.lock.annotation.DistributedLock;
import com.example.demo.lock.model.LockInfo;
import com.example.demo.lock.provider.concrete.FairLock;
import com.example.demo.lock.provider.concrete.ReadLock;
import com.example.demo.lock.provider.concrete.ReentrantLock;
import com.example.demo.lock.provider.concrete.WriteLock;
import org.aspectj.lang.JoinPoint;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class LockFactory {
@Autowired
private RedissonClient redissonClient;
@Autowired
private LockInfoProvider lockInfoProvider;
public Lock getLock(JoinPoint joinPoint, DistributedLock distributedLock) {
LockInfo lockInfo = lockInfoProvider.get(joinPoint, distributedLock);
switch (lockInfo.getType()) {
case REENTRANT:
return new ReentrantLock(redissonClient, lockInfo);
case FAIR:
return new FairLock(redissonClient, lockInfo);
case READ:
return new ReadLock(redissonClient, lockInfo);
case WRITE:
return new WriteLock(redissonClient, lockInfo);
default:
throw new UnsupportedOperationException("还未实现该类型的锁");
}
}
}
在需要用分布式锁的方法上面加 @DistributedLock 注解即可。
@PostMapping("/single")
@DistributedLock(name = "demo:product", key = "#param.id")
public Object single(@RequestBody @Valid LockDTO param) throws InterruptedException {
TimeUnit.SECONDS.sleep(20);
return "OK";
}