一个注解实现分布式锁

1、原始写法

我们平常使用redisson的分布式锁是怎么做的?

是不是基本都用的这个模板,既然是模板,那为何不把他抽出来呢?

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {try {...业务代码} finally {lock.unlock();}
}

2、抽出分布式锁工具类

我们可以抽出一个LockService方法,把锁的模板写在方法里,调用的时候只需要指定key,把锁内的代码块用supplier函数传进来

@Service
@Slf4j
public class LockService {@Autowiredprivate RedissonClient redissonClient;public <T> T executeWithLock(String key, int waitTime, TimeUnit unit, SupplierThrow<T> supplier) throws Throwable {RLock lock = redissonClient.getLock(key);boolean lockSuccess = lock.tryLock(waitTime, unit);if (!lockSuccess) {throw new BusinessException(CommonErrorEnum.LOCK_LIMIT);}try {return supplier.get();//执行锁内的代码逻辑} finally {lock.unlock();}}
}

使用起来就方便了

lockService.executeWithLock(key, 10, TimeUnit.SECONDS, ()->{//执行业务逻辑。。。。。return null;
});

如果我们不需要排队等锁,甚至还能重载方法减少两个参数。

lockService.executeWithLock(key, ()->{//执行业务逻辑。。。。。return null;
});

还能不能更简便呢?当然!

3、注解实现分布式锁

其实锁工具类已经是核心功能代码了,用注解只是为了使用方便。就像很多底层sdk,都是有接口调用的方法来实现核心功能,然后再加个注解让使用更加简便。

来想一想场景,我们的分布式锁很多时候都是加在最外层,也就是controller上,或者是service某个方法上。

我们通常加锁需要的key,都是由入参组装的。那是不是可以用el表达式来组装key呢?

1. 创建注解@RedissonLock

/*** 分布式锁注解*/
@Retention(RetentionPolicy.RUNTIME)//运行时生效
@Target(ElementType.METHOD)//作用在方法上
public @interface RedissonLock {/*** key的前缀,默认取方法全限定名,除非我们在不同方法上对同一个资源做分布式锁,就自己指定** @return key的前缀*/String prefixKey() default "";/*** springEl 表达式** @return 表达式*/String key();/*** 等待锁的时间,默认-1,不等待直接失败,redisson默认也是-1** @return 单位秒*/int waitTime() default -1;/*** 等待锁的时间单位,默认毫秒** @return 单位*/TimeUnit unit() default TimeUnit.MILLISECONDS;}

约定大于配置的思想,我们的大多数参数都是可以默认的。

很多时候我们的锁都是针对方法的,要锁同一处地方,调用同一个方法就好了,这样前缀可以直接默认根据类+方法名来实现,同样针对特例我们也提供了自己指定前缀的入口。

2. 实现切面RedissonLockAspect

@Slf4j
@Aspect
@Component
@Order(0)//确保比事务注解先执行,分布式锁在事务外
public class RedissonLockAspect {@Autowiredprivate LockService lockService;@Around("@annotation(com.annotation.RedissonLock)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();RedissonLock redissonLock = method.getAnnotation(RedissonLock.class);String prefix = StrUtil.isBlank(redissonLock.prefixKey()) ? SpElUtils.getMethodKey(method) : redissonLock.prefixKey();//默认方法限定名+注解排名(可能多个)String key = SpElUtils.parseSpEl(method, joinPoint.getArgs(), redissonLock.key());return lockService.executeWithLockThrows(prefix + ":" + key, redissonLock.waitTime(), redissonLock.unit(), joinPoint::proceed);}
}

切面其实很简单,构建key=前缀+el表达式,然后把参数都传进去,调用我们核心功能的工具类LockService

3. 使用

image-20231228161009680

使用起来就非常方便了,发奖的时候,我们需要对uid加锁,直接一个注解搞定。如果需要等待,再加个等待时间就行。

这里需要注意,分布式锁要在事务外层。所以我们锁的切面优先级要高一些。

4. 优化的思考

有的时候,key是由两个参数组成的,比如

@RedissonLock(key = "#uid+'_'+#itemId")
public void acquireItem(Long uid, Long itemId, String businessId) {//...
}

自己在el里面拼接就比较不方便。怎么去优化呢?,可以设置key为一个el表达式数组,给传多个参数就行了。

image-20231228161044354

@RedissonLock(key = {"#uid","#itemId"})
public void acquireItem(Long uid, Long itemId, String businessId) {//...
}

.(img-74yJqLyy-1703751249954)]

@RedissonLock(key = {"#uid","#itemId"})
public void acquireItem(Long uid, Long itemId, String businessId) {//...
}

由切面的代码去拼接两个key。项目目前没用到两个参数,等之后使用到,就会考虑加上该优化。很多代码没必要一步到位,要在迭代中保持小重构的意识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/305440.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023-12-23 组合总和 III和电话号码的字母组合

216. 组合总和 III 思路&#xff1a;使用回溯三部曲&#xff01;① 确认需要传入的参数以及返回值 ② 回溯的终止条件 ③ 单层搜索的逻辑&#xff01;这道题易错点在于单层的逻辑上的遍历起始位置以及回溯回退步骤里要执行的内容&#xff01; class Solution:def combinationSu…

Redis 是如何执行的?

文章目录 命令执行流程步骤一&#xff1a;用户输入一条命令步骤二&#xff1a;客户端先将命令转换成 Redis 协议&#xff0c;然后再通过 socket 连接发送给服务器端步骤三&#xff1a;服务器端接收到命令步骤四&#xff1a;执行前准备步骤五&#xff1a;执行最终命令&#xff0…

docker学习(二十一、network使用示例container、自定义)

文章目录 一、container应用示例1.需要共用同一个端口的服务&#xff0c;不适用container方式2.可用示例3.停掉共享源的容器&#xff0c;其他容器只有本地回环lo地址 总结 二、自定义网络应用示例默认bridge&#xff0c;容器间ip通信默认bridge&#xff0c;容器间服务名不通 自…

关于设计模式、Java基础面试题

前言 之前为了准备面试&#xff0c;收集整理了一些面试题。 本篇文章更新时间2023年12月27日。 最新的内容可以看我的原文&#xff1a;https://www.yuque.com/wfzx/ninzck/cbf0cxkrr6s1kniv 设计模式 单例共有几种写法&#xff1f; 细分起来就有9种&#xff1a;懒汉&#x…

常见算法(java版)

冒泡排序 每次从数组中找出最大值放在数组的后面去。 关键步骤 确定总共需要做几轮&#xff1a; 数组的长度-1。 每轮比较几次:数组的长度 - 第i轮。 当前位置大于后一个位置则交换数据。 选择排序 每轮选择当前位置&#xff0c;开始找出后面的较小值与该位置交换。 关键…

Keras多分类鸢尾花DEMO

完整的一个小demo&#xff1a; pandas1.2.4 numpy1.19.2 python3.9.2 import numpy as np import pandas as pd import matplotlib.pyplot as plt from pandas import DataFrame from scipy.io import loadmat from sklearn.model_selection import train_test_split impor…

【精选】vulnhub CTF5 NanoCMS漏洞 (青铜门笔记)

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

关于StartAI生图下载问题

最近小编常常收到一些小伙伴对StartAI生图的问题反馈&#xff0c;今天为大家同一解答吧&#xff01; Q1&#xff1a;小编小编&#xff0c;为什么我生图后下载图片在文件夹中显示空白呀&#xff1f; 小编&#xff1a;当前我们StartAI版本0.4.5在下载图片时还未添加保存类型&…

Apache Commons Pool的对象池技术

第1章&#xff1a;引言 咱们今天来聊聊一个在Java开发中超级实用&#xff0c;但又经常被忽视的技术——对象池技术。可能你们已经听说过“对象池”这个名词&#xff0c;但对它的具体作用和重要性还有些模糊。别急&#xff0c;小黑带你们一步步深入了解。 想象一下&#xff0c…

产品经理如何培养思维模式和创新能力?

作为一名产品经理&#xff0c;我们需要具备一定的思维模式和创新能力&#xff0c;以应对不断变化的市场和技术环境。在本文中&#xff0c;我将分享一些培养产品经理思维模式和创新能力的方法。 一、培养市场洞察力 作为产品经理&#xff0c;我们需要深入了解市场和用户需求&a…

9种卷积注意力机制创新方法汇总,含2024最新

今天咱们来聊聊卷积注意力机制。 相信各位在写论文的时候都苦恼过怎么更好地改模型&#xff0c;怎么更高效地提高模型的性能和泛化能力吧&#xff1f;我的建议是&#xff0c;不妨考虑考虑卷积注意力。 卷积注意力机制是一种通过关注输入数据中的不同部分来改进模型性能的方法…

前端子项目共用node_modules

项目目录结构如下 首先按上面的结构新建三个项目&#xff0c;有一定前端经验的都知道怎么处理&#xff0c;我就不多介绍了。 1&#xff0c;子项目1 package.json如下&#xff0c;我只安装了vue index.js如下 2&#xff0c;子项目2 package.json如下&#xff0c;我安装了…