基于注解和redisson实现redis分布锁

用过redis锁的同学,有没有感觉到redis锁的获取与关闭和事务的开启与提交很相似。那事务可以使用@Transitional注解方便的使用,那么我们为什么不把redis锁也通过注解的形式实现呢~

在实现的过程中,顺便一起引入redisson框架

1.先引入pom

 <dependency>
       <groupId>org.redisson</groupId>
       <artifactId>redisson</artifactId>
       <version>3.11.2</version>
 </dependency>

2.再配置给redisson配置redis连接

package com.example.demo.config;

import org.redisson.Redisson;
import org.redisson.config.Config;

/**
 * @author LWong
 * @date 2019/12/4/004
 */
public class RedissonManager {
    private static Config config = new Config();
    //声明redisso对象
    private static Redisson redisson = null;
    //实例化redisson
    static{

        config.useSingleServer().setAddress("redis://47.97.101.185:63791");
        //得到redisson对象
        redisson = (Redisson) Redisson.create(config);

    }

    //获取redisson对象的方法
    public static Redisson getRedisson(){
        return redisson;
    }
}

3.然后封装redis获取锁和释放锁的方法,这里是基于redisson的方法

package com.example.demo.util;

import com.example.demo.config.RedissonManager;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author LWong
 * @date 2019/12/4/004
 */
@Component
public class RedisLockUtil {

    //从配置类中获取Redisson对象
    private static Redisson redisson = RedissonManager.getRedisson();
    private static final String LOCK_TITLE = "redisLock_";

    //加锁
    public boolean acquire(String key, long expire, TimeUnit expireUnit) {
        //声明key对象
        key = LOCK_TITLE + key;
        //获取锁对象
        RLock mylock = redisson.getLock(key);
        //加锁,并且设置锁过期时间,防止死锁的产生
        mylock.lock(expire, expireUnit);
        System.err.println("======lock======" + Thread.currentThread().getName());
        //加锁成功
        return true;
    }

    //锁的释放
    public void release(String lockName) {
        //必须是和加锁时的同一个key
        String key = LOCK_TITLE + lockName;
        //获取所对象
        RLock mylock = redisson.getLock(key);
        //释放锁(解锁)
        mylock.unlock();
        System.err.println("======unlock======" + Thread.currentThread().getName());
    }
}

4.获取redisson连接之后,开始编写注解@RedisLock

package com.example.demo.util.lock;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author LWong
 * @date 2019/12/4
 */

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {

    String key() default "redisLockKey";

    int expireTime() default 30;//30秒

}

5.接下来通过aop实现注解功能

package com.example.demo.util.lock;

import com.example.demo.util.RedisLockUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author LWong
 * @date 2019/12/4/004
 * redis分布式锁处理 不适用与内部方法调用(this.)或者private
 */
@Component
@Aspect
@Order(9)
public class RedisLockAspect {

    @Autowired
    private RedisLockUtil redisUtil;

    @Pointcut("@annotation(com.example.demo.util.lock.RedisLock)")
    public void annotationPointcut() {
    }

    @Around("annotationPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获得当前访问的class
        Class<?> className = joinPoint.getTarget().getClass();
        // 获得访问的方法名
        String methodName = joinPoint.getSignature().getName();
        // 得到方法的参数的类型
        Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        Object[] args = joinPoint.getArgs();
        String key = "";
        // 默认30秒过期时间
        int expireTime = 30;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            method.setAccessible(true);
            // 判断是否存在@RedisLock注解
            if (method.isAnnotationPresent(RedisLock.class)) {
                RedisLock annotation = method.getAnnotation(RedisLock.class);
                key = getRedisKey(args, annotation.key());
                expireTime = getExpireTime(annotation);
            }
        } catch (Exception e) {
            throw new RuntimeException("redis分布式锁注解参数异常", e);
        }
        Object res = new Object();
        if (redisUtil.acquire(key, expireTime, TimeUnit.SECONDS)) {
            try {
                res = joinPoint.proceed();
                return res;
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                redisUtil.release(key);
            }
        }
        return res;
    }


    private int getExpireTime(RedisLock annotation) {
        return annotation.expireTime();
    }

    private String getRedisKey(Object[] args, String primalKey) {
        if (args.length == 0) {
            return primalKey;
        }
        // 获取#p0...集合
        List<String> keyList = getKeyParsList(primalKey);
        for (String keyName : keyList) {
            int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
            Object parValue = args[keyIndex];
            primalKey = primalKey.replace(keyName, String.valueOf(parValue));
        }
        return primalKey.replace("+", "").replace("'", "");
    }

    /**
     * 获取key中#p0中的参数名称
     *
     * @param key
     * @return
     */
    private static List<String> getKeyParsList(String key) {
        List<String> listPar = new ArrayList<>();
        if (key.contains("#")) {
            int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
            int indexNext = 0;
            String parName;
            int indexPre = key.indexOf("#");
            if (plusIndex > 0) {
                indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");
                parName = key.substring(indexPre, indexNext);
            } else {
                parName = key.substring(indexPre);
            }
            listPar.add(parName.trim());
            key = key.substring(indexNext + 1);
            if (key.contains("#")) {
                listPar.addAll(getKeyParsList(key));
            }
        }
        return listPar;
    }

}

6.最后编写测试方法

@GetMapping("/getLock")
@RedisLock(key = "test+#p0+#p1", expireTime = 30)
@ApiOperation("根据时间和访客数量查询列表")
public Resp getLock() throws InterruptedException {
    Thread.sleep(2000);
    return Resp.success();
}

输出结果,完成效果

======lock======http-nio-8888-exec-5
======unlock======http-nio-8888-exec-5
原创文章 96 获赞 201 访问量 67万+

猜你喜欢

转载自blog.csdn.net/sinat_29774479/article/details/103392305