基于注解实现Redis分布式锁

Yohann-study

基于注解实现分布式锁

思路

  • 使用Redisson实现分布式锁
  • 定义分布式锁注解
  • 基于SpEl生成锁的key

分布式锁注解

package com.yohann.boot.common.annotation;

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

/**
 * 分布式锁注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock {

    /**
     * 前缀
     */
    String prefix() default "";

    /**
     * SpEl表达式 key
     */
    String key() default "";

    /**
     * 等锁时间
     */
    long waitTime() default -1;

    /**
     * 释放时间
     */
    long leaseTime() default -1;

    /**
     * 时间单位
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

}

Redisson配置

package com.yohann.boot.config;

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;

/**
 * Redisson 配置
 */
@Configuration
public class RedissonConfig {

    @Bean
    @Primary
    public RedissonClient getRedissonClient(RedisProperties redisProperties) {
        Config config = new Config();
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
                .setDatabase(redisProperties.getDatabase())
                .setConnectionPoolSize(100)
                .setPingConnectionInterval(2000)
                .setRetryAttempts(3)
                .setRetryInterval(1000)
                .setConnectionMinimumIdleSize(100);
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            serverConfig.setPassword(redisProperties.getPassword());
        }
        config.setCodec(new JsonJacksonCodec());
        return Redisson.create(config);
    }
}

分布式锁切面实现

package com.yohann.boot.config.aspect;

import com.yohann.boot.common.annotation.Lock;
import lombok.extern.slf4j.Slf4j;
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.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
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.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author Yeung Wah
 * @since 2022/4/26
 */
@Slf4j
@Aspect
@Component
public class LockAspect {

    @Autowired
    private RedissonClient redissonClient;

    /**
     * SpEl解析器
     */
    private ExpressionParser parser = new SpelExpressionParser();

    @Pointcut("@annotation(com.yohann.boot.common.annotation.Lock)")
    public void lockAspect() {
    }

    @Around("lockAspect()")
    public Object log(ProceedingJoinPoint pjp) throws Throwable {
        //目标方法
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        String[] paramNames = signature.getParameterNames();
        Lock annotation = method.getAnnotation(Lock.class);

        //前缀生成
        String prefix = annotation.prefix();
        if (prefix.isEmpty()) {
            String className = pjp.getTarget().getClass().getName();
            String methodName = method.getName();
            prefix = String.join(":", className, methodName, Arrays.toString(paramNames));
        }

        //参数解析SpEl
        String key = annotation.key();
        Object[] args = pjp.getArgs();
        if (key.length() > 0 && args.length > 0) {
            //将参数名与参数值对应起来
            EvaluationContext context = new StandardEvaluationContext();
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }

            //根据SpEl表达式获取值
            Expression expression = parser.parseExpression(key);
            Object value = expression.getValue(context);
            key = Objects.toString(value, "");
        }

        //加锁
        long waitTime = annotation.waitTime(), leaseTime = annotation.leaseTime();
        TimeUnit timeUnit = annotation.timeUnit();
        String lockName = prefix + key;
        RLock lock = redissonClient.getLock(lockName);
        log.debug("get lock name: {}", lockName);
        boolean tryLock = lock.tryLock(waitTime, leaseTime, timeUnit);
        if (!tryLock) {
            throw new RuntimeException("get lock:" + lockName + " failed.");
        }
        try {
            return pjp.proceed();
        } finally {
            lock.unlock();
        }
    }

}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值