Redisson 分布式锁封装

2022-12-03 12:01:52 浏览数 (1)

项目用 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 "获取锁失败";
    });
}

区别

  • 第一种适用于方法级别的,代码更优雅舒服一些。
  • 第二种通用,整个方法全部代码块和部分代码块都可,业务拓展性更高。

0 人点赞