一个注解完美实现分布式锁(AOP)

前言

        学习过Spring的小伙伴都知道AOP的强大,本文将通过Redisson结合AOP,仅需一个注解就能实现分布式锁。 🍭 


不会使用aop和redisson的小伙伴可以参考:

【学习总结】使Aop实现自定义日志注解-CSDN博客

【学习总结】使用分布式锁和乐观锁解决“超卖”问题-CSDN博客

前提

有小伙伴可能会看不懂下面对key的一些操作,当key为null时,使用StringBuilder手动拼接key,不为null时,主要使用到了SpEl表达式。

  • 使用Spring Expression Language (SpEL)来支持在`@DLock`注解的`value`属性中定义动态key。 
  • 使用`StandardEvaluationContext`和`DefaultParameterNameDiscoverer`来解析方法参数名,并将它们作为变量存储在SpEL的上下文中。
  • 使用`SpelExpressionParser`来解析key字符串中的SpEL表达式,并获取最终的key值。

代码

自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DLock {
    //分布式锁的key
    String value() default "";
}

  value属性定义了分布式锁的key,默认为空,如果为空会根据当前注解的类及方法参数等生成key。

切面定义:

@Component
@Aspect
public class DistributedLockAspect {
    private final RedissonClient redisson;
    public DistributedLockAspect(RedissonClient redissonClient){
        this.redisson = redissonClient;;
    }

    //定义切面
    @Pointcut("@annotation(lock)")
    private void lockPointcut(DLock lock){}

    @Around("lockPointcut(lock)")
    public Object lockAround(ProceedingJoinPoint point,DLock lock) throws Throwable{
        //从ProceedingJoinPoint对象中获取目标方法的签名,并将其强制转换为MethodSignature类型
        MethodSignature signature = (MethodSignature)point.getSignature();
        //获取目标对象的方法
        Method method = signature.getMethod();
        //获取目标方法的参数数组
        Object[] args = point.getArgs();
        //定义分布式锁key
        String key = lock.value();
        if ("".equals(key)){
            //根据当前的类名+方法参数信息生成key
            key = configKey(signature.getDeclaringType(), method).replaceAll("[^a-zA-Z0-9]", "") ;
            System.out.println(key);
        }else {
            //支持SpEL表达式
            StandardEvaluationContext context = new StandardEvaluationContext();
            //将当前方法参数信息都存入到SpEl执行的上下文中
            DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
            String[] parameterNames = discoverer.getParameterNames(method);
            for (int i = 0,len = parameterNames.length; i < len; i++){
                context.setVariable(parameterNames[i],args[i]);
            }
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            key = expression.getValue(context,String.class);
        }
        //加锁
        RLock rLock = redisson.getLock(key);
        rLock.lock();
        try {
            //执行业务代码
            Object o = point.proceed();
            return o;
        }finally {
            rLock.unlock();
        }
    }

    private String configKey(Class<?> targetType, Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(targetType.getSimpleName());
        builder.append('#').append(method.getName()).append('(');
        for (Class<?> param : method.getParameterTypes()){
            builder.append(param.getSimpleName()).append(',');
        }
        if (method.getParameterTypes().length > 0){
            builder.deleteCharAt(builder.length() - 1);
        }
        return builder.append(')').toString();
    }
}

以上是基于注解实现分布式锁的核心类都定义完成了,接下来进行测试。

@GetMapping("/stock")
    @DLock("'user:' + #userId + ':' + #productId")
    @Transactional
    public String decStocks(@RequestParam Long userId, @RequestParam Integer productId){
        //查询商品信息
        Products product = productsService.getById(productId);
        //获取商品库存
        Integer stockQuantity = product.getStockQuantity();
        if (stockQuantity > 0){
            UpdateWrapper<Products> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("id",productId).setSql("stock_quantity = stock_quantity - 1");
            boolean result = productsService.update(updateWrapper);
            if (result){
                return "商品库存呢扣减成功";
            }
        }
        return "商品卖完了!";
    }

我就以一个简单的商品超卖的例子进行测试

假设库存为10

使用JMeter进行测试

测试结果

没有出现库存为负数的情况,非常成功,说明我们的锁注解起作用了。


总结

有兴趣的小伙伴可以试一试。

参考文章:

https://mp.weixin.qq.com/s/Bkhg74dE9HilE7PFtqj-5wicon-default.png?t=N7T8https://mp.weixin.qq.com/s/Bkhg74dE9HilE7PFtqj-5w

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杰不秃头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值