自定义注解实现redisson分布式锁(锁多个key)

1、编写自定义注解@lock

import java.lang.annotation.*;

/**
 * 锁自定义注解
 * @author 
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})//作用于参数或方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock {

    /**
     * 锁key
     * @return
     */
    String[] key() default {};

}

2、编写AOP切面

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.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

/**
 * 锁aop
 */
@Aspect
@Component
public class LockAspect {

    @Autowired
    private DistributedRedisLock distributedRedisLock;
    
    /**
     * 方法执行前锁,执行结束后释放锁,如果未获取到锁的key则不加锁
     * annotation内放自定义注解
     */
    @Around("@annotation(com.util.Lock)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try{
            Object object = null;
            //获取锁定的key
            Lock lockEntity = (((MethodSignature) joinPoint.getSignature()).getMethod()).getAnnotation(Lock.class);
           //key是否全部保存成功
            boolean acquire = true;
            List<String> keyArray = new ArrayList<>();
            try{
            //循环将注解中传递参数加锁
                for (String k:lockEntity.key()) {
                    String keyBySpeL = getKeyBySpeL(k, joinPoint);
                    keyArray.add(keyBySpeL);
                    if(StringUtils.isNotBlank(keyBySpeL)){
                        //加锁
                        boolean lockStatus =  distributedRedisLock.acquire(keyBySpeL);
                        if(!lockStatus){
                            acquire=false;
                        }
                    }
                }
                if(!acquire){
                    throw new Exception("同时操作一条数据,请稍后重试");
                }else{
                    //执行方法
                    object = joinPoint.proceed();
                }

            }catch (Exception e1){
                throw new Exception("同时操作一条数据,请稍后重试");
            }finally {
                //循环将所有锁释放
                for (String k:keyArray) {
                    try{
                        if(StringUtils.isNotBlank(k)){
                            distributedRedisLock.release(k);
                        }
                    }catch (Exception es){
                        continue;
                    }
                }
            }
            return object;
        }catch (Exception e){
            throw new Exception("同时操作一条数据,请稍后重试");
        }

    }


    /**
     * 用于SpEL表达式解析.
     */
    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    /**
     * 获取缓存的key
     * key 定义在注解上,支持SPEL表达式
     * @return
     */
    public String getKeyBySpeL(String spel, ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        String[] paramNames = defaultParameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = proceedingJoinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return String.valueOf(spelExpressionParser.parseExpression(spel).getValue(context));
    }

}

3、编写Redisson配置类RedissonConfig

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

/**
 * 添加Redisson的配置参数读取类RedissonConfig
 */
@Configuration
public class RedissonConfig {


    @Value("${redisson.config}")
    public String redissonConfig;
    /**
     * 读取锁的配置文件
     * @return
     * @throws IOException
     */
    @Bean
    public RedissonClient redisson() throws IOException {
        // 本例子使用的是yaml格式的配置文件,读取使用Config.fromYAML,如果是Json文件,则使用Config.fromJSON
        Config config = Config.fromYAML(redissonConfig);
        return Redisson.create(config);
    }

}

4、编写Redisson工具类

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * DistributedRedisLock
 */
@Component
public class DistributedRedisLock {

    @Autowired
    private RedissonClient redissonClient;

    private static final String LOCK_TITLE = "redisLock_";

    /**
     * 加锁
     * @param lockName
     * @return
     */
    public boolean acquire(String lockName){
        //声明key对象
        String key = LOCK_TITLE + lockName;
        //获取锁对象
        RLock mylock = redissonClient.getLock(key);
        //获取不到锁直接抛异常
        try {
        	//tryLock(等待时间,锁过期,时间单位),
            boolean b = mylock.tryLock(0, 1, TimeUnit.MINUTES);
            if(!b){
                throw new Exception("同时操作数据");
            }
        } catch (InterruptedException e) {
            throw new Exception("同时操作数据");
        }
        //下面是获取不到锁就等待,一直等到锁释放,根据自己情况考虑使用哪种
        //加锁,并且设置锁过期时间,防止死锁的产生
		//        mylock.lock(2, TimeUnit.MINUTES);
        //加锁成功
        return  true;
    }


    /**
     * 锁的释放
     * @param lockName
     * @return
     */
    public void release(String lockName){
        //必须是和加锁时的同一个key
        String key = LOCK_TITLE + lockName;
        //获取所对象
        RLock mylock = redissonClient.getLock(key);
        //释放锁(解锁)
        mylock.unlock();
    }

}

5、编写application.yml文件

#redisson配置文件
    redisson:
      config: |
        singleServerConfig:
          address: "redis://127.0.0.1:6379"
          password: 111111
          clientName: null
          database: 7 #选择使用哪个数据库0~15
          idleConnectionTimeout: 10000
          connectTimeout: 10000
          timeout: 3000
          retryAttempts: 3
          retryInterval: 1500
          subscriptionsPerConnection: 5
          subscriptionConnectionMinimumIdleSize: 1
          subscriptionConnectionPoolSize: 50
          connectionMinimumIdleSize: 32
          connectionPoolSize: 64
          dnsMonitoringInterval: 5000
          #dnsMonitoring: false

        threads: 0
        nettyThreads: 0
        codec:
          class: "org.redisson.codec.JsonJacksonCodec"
        transportMode: "NIO"

6、在需要加注解的地方使用

  @Lock(key = {"#dto.userId","#dto.goodsId"})
  public Result<Vo> saveOrUpdate(@Valid Dto dto) {
  }

你可能感兴趣的:(Java,分布式,java,spring)