redis锁aop实现

理解:基于aop编程离不开注解的加持

步骤:

  1. 编写注解,运行时可见
  2. 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){

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值