Spring AOP +redisson的redis分布式锁实现

博客介绍了如何使用 Redisson 框架实现一个基于注解的分布式锁,包括定义自定义注解 `DistributedLock`,设置锁的名称、等待时间和超时时间等参数,并在 AOP 切面中解析注解内容,进行加锁、解锁操作,确保并发控制的安全性。

      先上效果,rediskey 为第一个从参数的id  最大等待时间为5000毫秒,占用锁之后的自动释放时间为30秒,如果30秒内方法体执行完成,AOP代码会手动释放锁,但是对于写业务的人来说是透明的。

一丶先定义一个注解

package com.util.lock;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * @description: redis分布式锁
 * @createTime: 2022-06-08 09:43
 **/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
    /**
     * 锁的名称: 支持spel表达式 eg:#tenantDo.id
     * 默认为:类全限定名+'.'+方法名+':' + lockKey eg: com.lzkj.up.service.impl.UserService.getUser:10
     */
    String lockKey();

    /**
     * lockKeyPrefix为空时,lockKey使用默认前缀:类全限定名+'.'+方法名+':'
     * lockKeyPrefix不为空时,使用lockKeyPrefix作为前缀
     * @return
     */
    String lockKeyPrefix() default "";

    /**
     * 最长等待时间。默认3秒
     * 该字段只有当tryLock()返回true才有效。
     */
    long waitTime() default 3L;

    /**
     * 锁超时时间。默认5秒
     * 如果tryLock为false,且leaseTime设置为0及以下,会变成lock()
     */
    long leaseTime() default 30L;

    /**
     * 时间单位。默认为秒。
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

}

二丶实现aop切面逻辑,使用redission框架 

package com.lock;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: redis分布式锁 aop实现
 * @createTime: 2022-06-08 09:54
 **/
@Aspect
@Component
@Slf4j
public class DistributedLockAspect {

    @Autowired
    private Redisson redissonClient;

    @Around("@annotation(com.lzkj.up.util.lock.DistributedLock)")
    public Object doLock(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        DistributedLock distributedLock = method.getAnnotation(DistributedLock.class);
        String lockKey = distributedLock.lockKey();
        //获取方法的形参名数组,spring常用,rpc时不需要,反射也不需要。表达式中写形参名
        LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = nameDiscoverer.getParameterNames(method);
        String finalKey = "";
        String className = joinPoint.getSignature().getDeclaringTypeName();
        Map<String, Object> hashMap = new HashMap<>();
        if (parameterNames != null && parameterNames.length > 0) {
            //spel表达式解析
            ExpressionParser parser = new SpelExpressionParser();
            EvaluationContext context = new StandardEvaluationContext();
            Expression expression = parser.parseExpression(lockKey);
            //存储形参和传值的映射
            //获取方法传入的参数值
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < parameterNames.length; i++) {
                //建立形参名和值的映射关系,存入上下文当中,后面取出表达式带有的形参
                context.setVariable(parameterNames[i], args[i]);
                hashMap.put(parameterNames[i], args[i]);
            }
            //这里取字符串类型作为key
            String newKey = expression.getValue(context, String.class);
            finalKey = className + "." + method.getName() + ":" + newKey;
        } else {
            finalKey = className + "." + method.getName() + ":" + lockKey;
        }
         log.info("DistributedLock finalKey=[{}]开始上锁,参数为:{}", finalKey, JSONObject.toJSONString(hashMap));
        RLock lock = redissonClient.getLock(finalKey);
        boolean res = lock(lock, distributedLock);
        long currentThreadId = Thread.currentThread().getId();
        if (res) {
            log.info("获取锁成功:{}",currentThreadId);
            try {
                log.info("DistributedLock finalKey=[{}],方法运行结束", finalKey);
                return  joinPoint.proceed();
            } catch (Throwable e) {
                log.error("DistributedLock: finalKey: {} 业务异常", finalKey);
                throw new BusinessException("500",e.getMessage());
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            log.warn("DistributedLock: finalKey: {} 获取锁失败:{}",finalKey,currentThreadId);
            throw new BusinessException("400","获取锁失败,请稍后重试");
        }
    }


    private boolean lock(RLock lock, DistributedLock distributedLock)  {
        boolean result;
        // 上锁
        try {
            result = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), distributedLock.timeUnit());
        } catch (InterruptedException e) {
            log.error("DistributedLock InterruptedException",e);
            result = false;
        }
        return result;
    }

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值