理解:基于aop编程离不开注解的加持
步骤:
- 编写注解,运行时可见
- aop织入想要被加锁的方法
实现:
- 注解
package com.example.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisLockTarget {
/**请求唯一标识*/
String key() default "";
}
- aop织入
package com.example.annotation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class RedisLockAop {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private RedisLock redisLock;
@Around("@annotation(RedisLockTarget)")
public void doAround(ProceedingJoinPoint joinPoint){
Boolean redisLockFlag = false;
String redisKey = null;
// 获得当前访问的class
Class<?> clazz = joinPoint.getTarget().getClass();
// 获得访问的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的参数的类型
Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
try {
// 得到访问的方法对象
Method method = clazz.getMethod(methodName, argClass);
method.setAccessible(true);
if (method.isAnnotationPresent(RedisLockTarget.class)) {
RedisLockTarget annotation = method.getAnnotation(RedisLockTarget.class);
redisKey = getRedisKey(annotation);
//加锁
redisLockFlag = RedisLock.initRedisLock(redisTemplate,redisKey);
}
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
log.error(throwable.getMessage());
}finally {
//释放锁
if (redisLockFlag) {
if (log.isDebugEnabled()) {
log.debug(redisKey, "释放时间:", System.currentTimeMillis());
}
redisLock.unLock(redisKey);
}
}
}
/**
* 解析key对应的参数
*
*
* @param annotation
* @return
* @throws Exception
*/
private String getRedisKey(RedisLockTarget annotation) throws Exception {
String redisKey = annotation.key();
//String format = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
//log.info("分布式锁注解获取到的key值:{}",redisKey+":"+format);
return redisKey;
}
}
package com.example.annotation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.function.Function;
/**
* redis分布式锁
*/
@Slf4j
@Component
public class RedisLock {
public RedisLock() {
}
/**
* 日志打印对象
*/
private static Logger logger = LoggerFactory.getLogger(RedisLock.class);
/**
* 默认超时时间:2小时
* 单位:秒
*/
private static final int DEFAULT_LOCK_TIMEOUT = 60 * 60;
/**
* redis连接对象
*/
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* redis锁的list
*/
private List<String> lockKeyList;
/**
* Lock key path.
*/
private String lockKey;
/**
* redis的超时时间
* 单位:秒
*/
private int lockTimeout = DEFAULT_LOCK_TIMEOUT;
/**
* 单个key值的锁,默认超时时间(2小时)
*
* @param redisTemplate redis连接对象
* @param lockKey redis锁得key
*/
public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey) {
this.redisTemplate = redisTemplate;
this.lockKey = lockKey;
}
/**
* 单个key值的锁,自定义超时时间
*
* @param redisTemplate redis连接对象
* @param lockKey 锁的key值
* @param lockTimeout 超时时间:单位秒
*/
public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey, int lockTimeout) {
this(redisTemplate, lockKey);
this.lockTimeout = lockTimeout;
}
/**
* 锁一批数据,多个key,默认超时时间(2小时)
*
* @param redisTemplate redis连接对象
* @param lockKeyList key的集合
*/
public RedisLock(RedisTemplate<String, String> redisTemplate, List<String> lockKeyList) {
//已经加了锁的RedisLock,防止其中某笔数据已经锁住,后面需要释放
if (CollectionUtils.isNotEmpty(lockKeyList)) {
this.redisTemplate = redisTemplate;
this.lockKeyList = lockKeyList;
}
}
/**
* 锁一批数据,多个key,自定义超时时间
*
* @param redisTemplate redis连接对象
* @param lockKeyList key的集合
* @param lockTimeout 超时时间:单位秒
*/
public RedisLock(RedisTemplate<String, String> redisTemplate, List<String> lockKeyList, int lockTimeout) {
this(redisTemplate, lockKeyList);
this.lockTimeout = lockTimeout;
}
/**
* 获取对应key的值
*
* @param key key
* @return value
*/
private String get(final String key) {
String result = null;
try {
result = this.redisTemplate.execute((RedisCallback<String>) connection -> {
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] data = connection.get(Objects.requireNonNull(serializer.serialize(key)));
if (data == null) {
return null;
}
return serializer.deserialize(data);
});
} catch (Exception e) {
logger.error("get redis error, key : {}", key);
}
return result;
}
/**
* 获取旧值,并设置新值
*
* @param key redis的key
* @param value redis的新值
* @return redis的旧值
*/
private String getSet(final String key, final String value) {
String result = null;
try {
result = this.redisTemplate.execute((RedisCallback<String>) connection -> {
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] data = connection.getSet(Objects.requireNonNull(serializer.serialize(key)), Objects.requireNonNull(serializer.serialize(value)));
return serializer.deserialize(data);
});
} catch (Exception e) {
logger.error("getSet redis error, key : {}", key);
}
return result;
}
/**
* 移除key
*
* @param lockKey redis的key
*/
private void del(final String lockKey) {
try {
this.redisTemplate.execute((RedisCallback<Object>) connection -> {
StringRedisSerializer serializer = new StringRedisSerializer();
return connection.del(serializer.serialize(lockKey));
});
} catch (Exception e) {
logger.error("del redis error, key : {}", lockKey);
}
}
/**
* 设置key的超时时间
*
* @param lockKey redis的key
* @param expireTime 超时时间,单位秒
*/
private void expire(final String lockKey, final long expireTime) {
try {
this.redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.expire(Objects.requireNonNull(new StringRedisSerializer().serialize(lockKey)), expireTime));
} catch (Exception e) {
logger.error("expire redis error, key : {}", lockKey);
}
}
/**
* 批量设置key的超时时间
*
* @param lockkeyList 被锁的keys
* @param expireTime 失效时间
*/
private void batchExpire(final List<String> lockkeyList, final long expireTime) {
StringRedisSerializer serializer = new StringRedisSerializer();
try {
this.redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
for (String key : lockkeyList) {
connection.expire(Objects.requireNonNull(serializer.serialize(key)), expireTime);
}
return null;
}
});
} catch (Exception e) {
logger.error("expire redis error, key : {}", lockKey);
}
}
/**
* 设置键值对,并返回是否设置成功
*
* @param key 键
* @param value 值
* @return 处理结果
*/
private Boolean setNX(final String key, final String value) {
Boolean result = Boolean.FALSE;
try {
//执行
result = this.redisTemplate.execute((RedisCallback<Boolean>) connection -> {
StringRedisSerializer serializer = new StringRedisSerializer();
return connection.setNX(serializer.serialize(key), serializer.serialize(value));
});
} catch (Exception e) {
logger.error("setNX redis error, key : {}", key);
}
//返回结果
return result;
}
/**
* 批量设置键值对,并返回是否设置成功
*
* @param keysMap
* @return
*/
private Boolean mSetNx(final Map<byte[], byte[]> keysMap) {
Boolean result = false;
try {
//执行
result = this.redisTemplate.execute((RedisCallback<Boolean>) connection -> {
return connection.mSetNX(keysMap);
});
} catch (Exception e) {
logger.error("setNX redis error, key : {}", keysMap);
}
return result;
}
/**
* 加锁
* 取到锁加锁,取不到锁就返回
*
* @return 是否加锁成功
*/
public synchronized boolean lock() {
//锁释放时间
long lockTime = currtTimeForRedis() + this.lockTimeout;
//设置值,并判断设置结果
if (setNX(this.lockKey, String.valueOf(lockTime))) {
//设置超时时间,释放内存
expire(this.lockKey, this.lockTimeout);
return true;
} else {
//获取redis里面的时间
Object result = get(this.lockKey);
Long currtLockTimeoutStr = result == null ? null : Long.parseLong(result.toString());
//锁已经失效
if (currtLockTimeoutStr != null && currtLockTimeoutStr < currtTimeForRedis()) {
//判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行
//获取上一个锁到期时间,并设置现在的锁到期时间
Long oldLockTimeoutStr = Long.valueOf(getSet(this.lockKey, String.valueOf(lockTime)));
if (oldLockTimeoutStr.equals(currtLockTimeoutStr)) {
//多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁
//设置超时间,释放内存
expire(this.lockKey, this.lockTimeout);
//返回加锁时间
return true;
}
}
}
return false;
}
/**
* 解锁
*/
public synchronized void unLock() {
//获取redis中设置的时间
String result = get(this.lockKey);
Long currtLockTimeoutStr = result == null ? null : Long.valueOf(result);
if (currtLockTimeoutStr != null) {
this.del(this.lockKey);
}
}
/**
* 解锁
*/
public synchronized void unLock(String lockKey) {
//获取redis中设置的时间
String result = get(lockKey);
Long currtLockTimeoutStr = result == null ? null : Long.valueOf(result);
if (currtLockTimeoutStr != null) {
this.del(lockKey);
}
}
/**
* 多服务器集群,使用下面的方法,代替System.currentTimeMillis(),获取redis时间,避免多服务的时间不一致问题!!!
*
* @return 当前redis服务的时间秒
*/
private long currtTimeForRedis() {
return this.redisTemplate.execute((RedisCallback<Long>) redisConnection -> redisConnection.time() / 1000);
}
/**
* 锁List
*
* @return 锁的结果
*/
public boolean lockList() {
Boolean result = false;
if (CollectionUtils.isEmpty(this.lockKeyList)) {
//change by shencai.li@hand-china.com
//当列表为空时应返回true
return true;
}
//临时存放lock,方便后面锁有冲突的释放
if (!CollectionUtils.isEmpty(this.lockKeyList)) {
//锁释放时间
long lockTime = currtTimeForRedis() + this.lockTimeout;
Map<byte[], byte[]> keysMap = new HashMap<>();
for (String key : this.lockKeyList) {
StringRedisSerializer serializer = new StringRedisSerializer();
lockTime = lockTime + 1;
keysMap.put(serializer.serialize(key), serializer.serialize(String.valueOf(lockTime)));
}
//加锁
if (mSetNx(keysMap)) {
//设置失效时间
batchExpire(this.lockKeyList, this.lockTimeout);
result=true;
}
}
return result;
}
/**
* 批量解锁
*/
public void unLockList() {
if (CollectionUtils.isNotEmpty(this.lockKeyList)) {
this.redisTemplate.delete(this.lockKeyList);
}
}
/**
* 单个分布式锁方法
*
* @param redisTemplate 缓存对象
* @param lockKey 锁Key值
* @throws InterruptedException 抛出异常处理
*/
public static Boolean initRedisLock(RedisTemplate<String, String> redisTemplate, String lockKey) throws InterruptedException {
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, lockKey);
redisLockFlag = redisLock.lock();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(lockKey, "Lock Time:", System.currentTimeMillis());
}
} else {
throw new RuntimeException("数据正在处理中,redis锁:["+lockKey+"]");
}
}catch (Exception e){
throw new RuntimeException("加锁异常:"+e.getMessage());
}
return redisLockFlag;
}
/**
* 单个分布式锁方法
*
* @param redisTemplate 缓存对象
* @param lockKey 锁Key值
* @param voidFunction 执行方法 (不返回值)
* @throws InterruptedException 抛出异常处理
*/
public static void initRedisLock(RedisTemplate<String, String> redisTemplate, String lockKey, VoidFunction
voidFunction) throws InterruptedException {
log.info("=========redis锁===>>>>>>>>>>>>>>"+lockKey);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, lockKey);
redisLockFlag = redisLock.lock();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(lockKey, "Lock Time:", System.currentTimeMillis());
}
voidFunction.apply();
} else {
throw new RuntimeException("数据正在处理中,redis锁:["+lockKey+"]");
}
} finally {
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(lockKey, "释放时间:", System.currentTimeMillis());
}
redisLock.unLock();
}
}
}
/**
* 多个分布式锁方法
*
* @param redisTemplate 缓存对象
* @param redisKeyList 锁Key值集合
* @param voidFunction 执行方法 (不返回值)
* @throws InterruptedException 抛出异常处理(redis连接异常不做处理)
*/
public static void initRedisLock(RedisTemplate<String, String> redisTemplate, List<String> redisKeyList, VoidFunction voidFunction) throws
InterruptedException {
log.info("=========redis锁===>>>>>>>>>>>>>>"+redisKeyList);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKeyList);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(redisKeyList.toString(), "Lock Time:", System.currentTimeMillis());
}
voidFunction.apply();
} else {
throw new RuntimeException("数据正在处理中,redis锁:"+redisKeyList);
}
} finally {
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(redisKeyList.toString(), "unLock Time:", System.currentTimeMillis());
}
redisLock.unLockList();
}
}
}
/**
* 单个分布式锁方法
*
* @param redisTemplate 缓存对象
* @param redisKey 锁Key值
* @param resultObject 返回结果
* @param voidFunction 执行方法 (返回结果对象)
* @return
* @throws InterruptedException 抛出异常处理(redis连接异常不做处理)
*/
public static <T, R> R initRedisLock(RedisTemplate<String, String> redisTemplate, String redisKey, Object resultObject, Function<T, R> voidFunction) {
log.info("=========redis锁===>>>>>>>>>>>>>>"+redisKey);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKey);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug( redisKey, "Lock Time:", System.currentTimeMillis());
}
return voidFunction.apply((T) resultObject);
} else {
throw new RuntimeException("数据正在处理中,redis锁:"+redisKey);
}
} finally {
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug( redisKey, "unLock Time:", System.currentTimeMillis());
}
redisLock.unLockList();
}
}
}
/**
* 单个分布式锁方法(存在则过滤)
*
* @param redisTemplate 缓存对象
* @param redisKey 锁Key值
* @param resultObject 返回结果
* @param voidFunction 执行方法 (返回结果对象)
* @return
*/
public static <T, R> R initRedisLockFilter(RedisTemplate<String, String> redisTemplate, String redisKey, Object resultObject, Function<T, R> voidFunction) {
log.info("=========redis锁===>>>>>>>>>>>>>>"+redisKey);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKey);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(redisKey, "Lock Time:", System.currentTimeMillis());
}
return voidFunction.apply((T) resultObject);
}
} finally {
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug( redisKey, "unLock Time::", System.currentTimeMillis());
}
redisLock.unLockList();
}
}
return null;
}
/**
* 多个分布式锁方法
*
* @param redisTemplate 缓存对象
* @param redisKeyList 锁Key值集合
* @param resultList 返回结果集合
* @param voidFunction 执行方法 (返回结果对象)
* @return
*/
public static <T, R> List<R> initRedisLock(RedisTemplate<String, String> redisTemplate, List<String> redisKeyList, List<T> resultList, Function<List<T>, List<R>> voidFunction) {
log.info("=========redis锁===>>>>>>>>>>>>>>"+redisKeyList);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKeyList);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(redisKeyList.toString(), "Lock time:", System.currentTimeMillis());
}
return voidFunction.apply(resultList);
} else {
throw new RuntimeException("数据正在处理中,redis锁:"+redisKeyList);
}
} finally {
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug( redisKeyList.toString(), "unLock Time:", System.currentTimeMillis());
}
redisLock.unLockList();
}
}
}
/**
* 多个分布式锁方法(存在则过滤)
*
* @param redisTemplate 缓存对象
* @param redisKeyList 锁Key值集合
* @param resultList 返回结果集合
* @param voidFunction 执行方法 (返回结果对象)
* @return
* @throws InterruptedException 抛出异常处理(redis连接异常不做处理)
*/
public static <T, R> List<R> initRedisLockFilter(RedisTemplate<String, String> redisTemplate, List<String> redisKeyList, List<T> resultList, Function<List<T>, List<R>> voidFunction) {
log.info("=========redis锁===>>>>>>>>>>>>>>"+redisKeyList);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKeyList);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug(redisKeyList.toString(), "Lock Time:", System.currentTimeMillis());
}
return voidFunction.apply(resultList);
}
} finally {
if (redisLockFlag) {
if (logger.isDebugEnabled()) {
logger.debug( redisKeyList.toString(), "unLock Time:", System.currentTimeMillis());
}
redisLock.unLockList();
}
}
return null;
}
/**
* 多个分布式锁方法
*
* @param redisTemplate 缓存对象
* @param redisKeyList 锁Key值集合
* @param inParam Function<T,R> 传入参数
* @param voidFunction 执行方法 (返回结果对象)
* @return
* @throws InterruptedException 抛出异常处理(redis连接异常不做处理)
*/
public static <T, R> R initRedisLock(RedisTemplate<String, String> redisTemplate, List<String> redisKeyList, T inParam, Function<T, R> voidFunction) {
log.info("=========redis锁===>>>>>>>>>>>>>>"+redisKeyList);
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKeyList);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
return voidFunction.apply(inParam);
} else {
throw new RuntimeException("数据正在处理中,redis锁:"+redisKeyList);
}
} finally {
if (redisLockFlag) {
redisLock.unLockList();
}
}
}
/**
* 多个分布式锁方法(存在则过滤)
*
* @param redisTemplate 缓存对象
* @param redisKeyList 锁Key值集合
* @param inParam Function<T,R> 传入参数
* @param voidFunction 执行方法 (返回结果对象)
* @return
* @throws InterruptedException 抛出异常处理(redis连接异常不做处理)
*/
public static <T, R> R initRedisLockFilter(RedisTemplate<String, String> redisTemplate, List<String> redisKeyList, T inParam, Function<T, R> voidFunction) {
//redis锁对象
RedisLock redisLock = null;
Boolean redisLockFlag = false;
try {
redisLock = new RedisLock(redisTemplate, redisKeyList);
redisLockFlag = redisLock.lockList();
if (redisLockFlag) {
return voidFunction.apply(inParam);
}
} finally {
if (redisLockFlag) {
redisLock.unLockList();
}
}
return null;
}
}
- 业务代码
@GetMapping("/queryForeach")
@RedisLockTarget(key="test-queryForeach")
public void queryForeach(User user,String str){
}