SpringBoot整合Redisson分布式锁并实现AOP使用

SpringBoot整合Redisson的AOP注解实现, 使用注解加锁, 简化操作

Redisson官网: https://redisson.org/

Redisson+AOP实现
1. SpringBoot导包

具体版本对应可以参考官网连接 https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter

此处我使用的2.2.5的版本

 <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.2.5.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.16.7</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
2. 配置类编写

此处针对单机的redis进行配置, 比较简洁

RedisConfig.java

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        return Redisson.create(config);
    }
}

application.yaml

spring:
  redis:
    port: 6379
    database: 0
    host: localhost
3. 编写注解类
/**
 * @author wangdi
 * @date 21-7-8
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisDistributedLock {

    /**
     * 要锁哪几个位置的参数,默认不锁参数
     * (如果锁参数, 需要指定参数的索引比如锁第一个参数和第二个参数则传{0, 1}
     * 锁参数之后, 锁的key就会拼接此参数
     */
    int[] lockIndex() default {-1};

    /**
     * 默认包名加方法名
     *
     * @return
     */
    String key() default "";

    /**
     * 过期时间 单位:毫秒
     * <pre>
     *     过期时间一定是要长于业务的执行时间.
     * </pre>
     */
    long expire() default 30000;

    /**
     * 获取锁超时时间 单位:毫秒
     * <pre>
     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下.
     * </pre>
     */
    long timeout() default 3000;


    /**
     * 时间类型,默认毫秒
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

}
4. 编写切面类
/**
 * @author wangdi
 * @date 21-7-8
 */
@Configuration
@Aspect
@Slf4j
public class RedisLockAop {

    @Resource
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.wd.annotation.RedisDistributedLock)")
    public void myAdvice() {
    }

    @Around("myAdvice()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取注解
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        RedisDistributedLock annotation = signature.getMethod().getAnnotation(RedisDistributedLock.class);

        // 生成key
        StringBuilder keyBuilder = new StringBuilder(getKey(proceedingJoinPoint, annotation));

        // 如果锁参数, 需要将参数拼接到key上
        Object[] args = proceedingJoinPoint.getArgs();
        int[] lockIndex = annotation.lockIndex();
        if (lockIndex.length > 0 && lockIndex[0] >= 0) {
            for (int index : lockIndex) {
                if (index >= args.length || index < 0) {
                    throw new RuntimeException("参数索引lockIndex: " + index + " 异常");
                }
                keyBuilder.append(".").append(args[index].toString());
            }
        }

        // 防止key值太长,用根据其生成的hash值做key
        // String lockKey = DigestUtils.md5DigestAsHex(keyBuilder.toString().getBytes());
        String key = keyBuilder.toString();


        Boolean success = null;
        RLock lock = redissonClient.getLock(key);
        try {
            //lock提供带timeout参数,timeout结束强制解锁,防止死锁
            success = lock.tryLock(annotation.timeout(), annotation.expire(), annotation.timeUnit());
            if (success) {
                log.info(Thread.currentThread().getName() + " 加锁成功");
                // 放行方法执行
                return proceedingJoinPoint.proceed();
            }
            log.info(Thread.currentThread().getName() + " 加锁失败");
            throw new RuntimeException("操作频繁, 稍后重试"); // 此处可以用return 返回错误 需要跟切的方法的返回值保持一致
        } catch (Exception e) {
            throw e;
        } finally {
            if (success) {
                lock.unlock();
                log.info(Thread.currentThread().getName() + " 解锁成功");
            }
        }
    }

    private String getKey(ProceedingJoinPoint proceedingJoinPoint, RedisDistributedLock annotation) {
        if (!StringUtils.isEmpty(annotation.key())) {
            return annotation.key();
        }
        return proceedingJoinPoint.getSignature().getDeclaringTypeName() + "." + proceedingJoinPoint.getSignature()
                .getName();
    }
}
5. 测试
    /**
     * 手动加锁模拟 设置锁的时间为300秒 5分钟, 在5分钟之内若方法没有执行完成则自动解锁, 获取锁的等待时间为2秒
     */
    @GetMapping("/test")
    @RedisDistributedLock(timeUnit = TimeUnit.SECONDS, expire = 300, timeout = 2)
    public String test(String id, String name) {
        System.out.println("id = [" + id + "], name = [" + name + "]");
        if (id.equals("1"))
            throw new RuntimeException("出现异常");
        try {
            Thread.sleep(2 * 60 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "0";
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值