引言

在处理高并发的Web应用中,一种常见的问题就是如何保证数据的一致性。为了解决这个问题,我们通常会使用锁。然而,当我们的应用部署在多台服务器上时,单机锁就无法满足我们的需求了,我们需要一个分布式锁。本文将介绍如何使用Redisson实现一个基于注解的Redis分布式锁(解决幂等性问题)。

添加Redisson依赖

首先,我们需要在项目中添加Redisson的依赖。

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.6.5</version>
</dependency>

创建锁注解

接下来,我们需要创建一个用于加锁的注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HasRedissonLock {
    /**
     * 锁的Key
     */
    String value();
    /**
     * 锁的过期时间
     */
    int leaseTime() default 10;
}

实现注解处理器

然后,我们需要创建一个注解处理器,用于处理带有 @HasRedissonLock 注解的方法。

@Aspect
@Component
public class RedissonLock {
    @Autowired
    private RedissonClient redissonClient;

    @Around("execution(public * *(..)) && @annotation(com.app.aop.has.HasRedissonLock)")
    public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取登录用户ID,这里假设你有相应的方法获取用户ID
        Long loginUserId = TokenUtils.getRequestUserId();
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取注解
        HasRedissonLock hasRedissonLock = method.getDeclaredAnnotation(HasRedissonLock.class);
        // 锁的Key
        String lockKey = hasRedissonLock.value() + ":" + loginUserId;
        // 锁的过期时间
        long leaseTime = hasRedissonLock.leaseTime();
        // 获取分布式锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 尝试获取锁
        boolean isLocked = false;
        try {
            isLocked = lock.tryLock(leaseTime, TimeUnit.SECONDS);
            if (!isLocked) throw new BusinessException("请勿重复请求");
            // 执行目标方法
            return joinPoint.proceed();
        } finally {
            // 释放锁
            if (isLocked) lock.unlock();
        }
    }
}

在方法上使用注解

最后,我们可以在需要加锁的方法上使用 @HasRedissonLock 注解。

    @HasRedissonLock("save_user")
    @ApiOperation("添加用户")
    @PostMapping()
    public ApiResult<?> add(@RequestBody User user) {
        user.setStatus(0);
        user.setPassword(userService.encodePassword(user.getPassword()));
        if (userService.saveUser(user)) {
            return success("添加成功");
        }
        return fail("添加失败");
    }

总结

通过以上步骤,我们实现了一个基于注解的分布式锁。这种方法的优点是,我们不需要在每个需要加锁的方法中添加重复的锁管理代码。