一、示例一:使用AOP实现Redis分布式锁

1、在需要添加同步控制的业务对象中,首先引入redissonClient;
2、在具体业务方法上创建锁对象 redissonClient.getLock;
(getLock中的参数是自己根据业务拼接的字符串,整个系统中如果遇到相同字符串的锁定变量信息,将同步处理)
3、对同步块采用封装处理;

注:getLock与tryLock区别:
RLock lock=redissonClient.getLock():创建锁对象;
lock.tryLock():尝试获取锁;

public class TestReentrantLock {
    @Autowired
    private RedissonClient redissonClient;
    RLock lock = redissonClient.getLock(${锁定变量}); 
    try{
        if (lock.tryLock(10, TimeUnit.SECONDS)) {//尝试获取锁,如果超过10秒,进入异常流程
            try {
                //获得锁,进行业务处理(处理过程中同样的SceneType.VALIDATE_MOBILE.name()+uuid 值在全局内不能再次进入,保证同步性)
                //业务代码块 
            } catch (Exception e) {
                //业务报错异常处理分支

            } finally {
                lock.unlock();// 释放锁
            }
        } else {
            //获取锁超时处理分支
            //可以返回可控的处理信息 例如:"系统繁忙,请过后尝试!"
        }
    } catch(InterruptedException e){
        // trylock 线程阻塞Interrupt
        // 静默处理!
    }
}

二、示例二:使用AOP实现Redis分布式锁

第一步:自定义切面类:

@Component
@Aspect
@Slf4j
public class RedisLockAspect {
    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisProperties redisProperties;

    @Around("@annotation(com.asd.core.annotation.RedisLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        Method proxyMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();
        Method realMethod = joinPoint.getTarget().getClass().getDeclaredMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
        RedisLock redisLock = realMethod.getAnnotation(RedisLock.class);
        if (joinPoint.getArgs().length>0&&joinPoint.getArgs()[0] instanceof UserDTO){
            String certNo=((UserDTO)joinPoint.getArgs()[0]).getCertNo();
            //Redis分布式锁,同一用户同一时间只能单一查询
            RLock lock=null;
            try{
                lock = redissonClient.getLock(redisLock.prefix()+realMethod.getName()+":"+certNo);
                log.debug("lock:{}",lock.getName());

                if (!lock.tryLock(-1, redisProperties.getLockExpireInSeconds(), TimeUnit.SECONDS)){
                    throw new SametimeQueryException();
                }
            }catch (RedisException e){
                log.error("Redis异常,加锁失败",e);
            }
            try {
                return joinPoint.proceed();
            }finally {
                try{
                    if (lock!=null){
                        lock.unlock();
                    }
                }catch (RedisException e){
                    log.error("Redis异常,解锁失败",e);
                }
            }
        }

        //一般不会走到这步,除非方法参数异常
        throw new UnknownException();
    }
}

第二步:自定义切面注解:

/**
 * Redis分布式锁
 * (该注解用于方法上可以实现分布式同步锁)
 */
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
    String prefix() default "shop:Lock:";
    long waitSecond() default -1;
    long expireTime() default 10;
    TimeUnit unit() default TimeUnit.SECONDS;
}

第三步:业务类中使用切面注解:
查询时:同一用户同一时间只能单一查询;

public class QueryServiceImpl implements QueryService{
    @RedisLock
    @Override
    public User queryUser(String name){
        ...
    }
}

注意一: tryLock()方法介绍:

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待;具有三种构造方法:①无参;②包含 失效时间、单位 两个参数的构造方法;③包含 等待时间,失效时间,单位三个参数的构造方法; 示例二的自定义切面类中用的是第三种。

public class TestReentrantLock {
    public void testReentrantLock(RedissonClient redisson){
        RLock lock = redisson.getLock("anyLock");
        try{
            //1.无参 (最常见的使用方法)
            //lock.lock();
            
            //2.两个参数:失效时间,单位 (支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁)
            //lock.lock(10, TimeUnit.SECONDS);
            
            //3.三个参数:等待时间,失效时间,单位 (尝试加锁,最多等待3秒,上锁以后10秒自动解锁)
            boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if(res){ //成功
                // do your business
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

注意二:1. RedisProperties.java:

@Component
@Data
@ConfigurationProperties("asd.core.redis")
public class RedisProperties {
    private Integer lockExpireInSeconds;
    private Integer rateLimiterTimeout;
}

2. application.yml:

pboc:
  core:
    redis:
      lock-expire-in-seconds: 30
      rate-limiter-timeout: 10

3. 对于@ConfigurationProperties(“asd.core.redis”)的使用:

在SpringBoot使用@ConfigurationProperties注解读取yml/properties配置文件参数:在配置文件中所配置的参数都正确的注入到Java类对象中,而类中存在,但配置文件中没有配置的属性值默认为null。