redis锁基于注解实现

1、背景

在日常开发中,为了防止高并发,在不依赖过多的中间件的情况下,最常使用的分布式锁之一是 Redis锁。使用Redis锁就不得不面临一个问题,就是在业务代码中要控制Redis加锁、释放锁等等,对代码的侵入性较强。本文采用注解的方式为方法体增加分布式锁,唯一标识从方法参数中动态获取。

2、优点

  1. 无侵入。通过注解实现加锁和释放锁,代码中只需关注业务实现,无须关心“锁”问题,避免代码侵入。
  2. 无死锁。即使某一线程中断没能释放锁,在到达指定的时间后,程序会自动释放锁。
  3. 锁唯一独有。加锁和释放锁必须由同一线程执行,不会出现A线程加锁后,B线程将锁释放。
  4. 支持多种方式传参做key。通过注解指定参数名,通过反射,动态获得key。
  5. 线程间锁互斥。在同一时间内,仅有一个线程持有锁,避免多个线程同时执行逻辑,出现并发情况。

3、实现方式

  1. 通过AOP切面对方法执行前和方法执行后加锁和释放锁
  2. 注解参数传入key保证唯一,通过key加锁保证key不会重复
  3. 默认释放锁时间30分钟,可以通过注解参数设置不同方法的默认释放锁时间防止死锁

4、具体实现

分布式锁注解,适用于方法 通过key定义请求唯一值,防止业务重复提交, 业务重复提交判断需要在方法内判断,注解能拦截正在提交中不能再次请求唯一值。 key中禁止使用 ” p # + . “字符 这几个字符作为解析key的关键字

4.1、定义一个注解,接收参数key

import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisLockTarget {
    /**请求唯一标识*/
    String key() default "";
}

4.2、定义aop切入注解,获取有注解的方法

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;
import java.util.Date;

@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);
                //加锁
                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();
            }
        }
    }
    /**
     * 解析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+":"+format;
    }
}

5、总结

利用注解加切面来动态的加锁,无侵入无死锁锁唯一独有,可以方便开发。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值