springboot中使用注解实现分布式锁

下面将详细介绍如何在 Spring Boot 里借助注解实现分布式锁,以login_lock:作为锁的 key 前缀,使用请求参数里的phone值作为 key,等待时间设为 0 秒,锁的持续时间为 10 秒。我们会使用 Redis 来实现分布式锁,同时借助 Spring AOP 与自定义注解达成基于注解的锁机制。

1. 添加依赖

pom.xml文件中添加必要的依赖,包括 Spring Boot Redis 和 Spring Boot AOP:

xml

<dependencies><!-- Spring Boot Redis 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Spring Boot AOP 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
</dependencies>

2. 配置 Redis

application.properties或者application.yml中配置 Redis 连接信息,以application.yml为例:

yaml

spring:redis:host: localhostport: 6379# 若 Redis 有密码,需添加此项# password: yourpassword

3. 定义分布式锁注解

创建自定义注解DistributedLock,用于标记需要加锁的方法:

java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {String keyPrefix() default "login_lock:";String keyField() default "phone";long waitTime() default 0;long leaseTime() default 10;
}

4. 实现分布式锁切面

创建切面类DistributedLockAspect,处理加锁和解锁逻辑:

java

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.Collections;
import java.util.concurrent.TimeUnit;@Aspect
@Component
public class DistributedLockAspect {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final RedisScript<Long> UNLOCK_SCRIPT;static {StringBuilder script = new StringBuilder();script.append("if redis.call('get', KEYS[1]) == ARGV[1] then");script.append("    return redis.call('del', KEYS[1])");script.append("else");script.append("    return 0");script.append("end");UNLOCK_SCRIPT = new DefaultRedisScript<>(script.toString(), Long.class);}@Around("@annotation(com.example.demo.DistributedLock)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();DistributedLock distributedLock = signature.getMethod().getAnnotation(DistributedLock.class);String keyPrefix = distributedLock.keyPrefix();String keyField = distributedLock.keyField();long waitTime = distributedLock.waitTime();long leaseTime = distributedLock.leaseTime();Object[] args = joinPoint.getArgs();String keyValue = null;for (Object arg : args) {try {Field field = arg.getClass().getDeclaredField(keyField);field.setAccessible(true);keyValue = String.valueOf(field.get(arg));break;} catch (NoSuchFieldException | IllegalAccessException e) {// 忽略异常,继续尝试下一个参数}}if (keyValue == null) {throw new IllegalArgumentException("Could not find the key field in the method arguments.");}String lockKey = keyPrefix + keyValue;String requestId = java.util.UUID.randomUUID().toString();long startTime = System.currentTimeMillis();boolean locked = false;do {locked = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, leaseTime, TimeUnit.SECONDS);if (locked) {break;}// 检查是否超过等待时间if (System.currentTimeMillis() - startTime >= waitTime * 1000) {break;}// 短暂休眠后重试Thread.sleep(100);} while (true);if (!locked) {throw new RuntimeException("Failed to acquire the lock after waiting.");}try {return joinPoint.proceed();} finally {// 使用 Lua 脚本释放锁,保证原子性redisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(lockKey), requestId);}}
}

5. 使用分布式锁注解

在需要加锁的方法上添加DistributedLock注解:

java

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LoginController {@PostMapping("/login")@DistributedLockpublic String login(@RequestBody User user) {// 模拟业务逻辑try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return "Login success";}
}class User {private String phone;public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}
}

代码解释

  • DistributedLock注解:用于标记需要加锁的方法,可通过keyPrefixkeyFieldwaitTimeleaseTime属性配置锁的相关信息。
  • DistributedLockAspect切面类
    • 运用@Around注解拦截所有标记了DistributedLock注解的方法。
    • 从方法参数里提取phone值,组合成 Redis 锁的 key。
    • 借助redisTemplate.opsForValue().setIfAbsent方法尝试获取锁,若获取失败则抛出异常。
    • 使用 Lua 脚本释放锁,确保操作的原子性,防止误删其他线程的锁。
  • LoginController控制器:在login方法上添加DistributedLock注解,保证同一手机号在同一时间只有一个请求能进入该方法。

通过以上步骤,你就能在 Spring Boot 项目中使用注解实现分布式锁。

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

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

相关文章

UART通信详解基于IMX6ULL实现

目录 2.1串口连接 2.2工作原理||数据通讯格式 2.2.1起始位 2.2.2数据位 2.2.3奇偶校验位 2.2.4停止位 2.2.5协议层 2.2.6波特率 2.2.7数据校验 2.3传输步骤 2.4IMX6ULL驱动开发-基于UART框架发送/接收串口数据 2.4.1在设备树中添加uart3子节点 2.4.2编写串口测试程…

Java运行时数据区

JVM主要流程包括三部分&#xff1a; 首先是 ClassLoard 类加载器&#xff0c;加载数据源文件到jvm当中然后将加载好的数据存放在运行时数据区最后由引擎进行解释和编译的工作 1.Java 内存区域&#xff1a; 1.1 程序计数器 程序计数器&#xff08;Program Counter Register&a…

Vue 响应式渲染 - 条件渲染

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue响应式渲染 - 条件渲染 目录 条件渲染 v-if v-if-else 模版template 物流状态显示判断 设置数据 不同状态渲染 总结 条件渲染 v-if 使用Vue条件判断显示和 隐藏。 示例如下&#xff1a; <!DOCTYPE html> …

如何利用DeepSeek挖掘龙头股

如何利用DeepSeek挖掘龙头股&#xff0c;当我们输入这样的问题&#xff0c;看看DeepSeek是如何归纳总结的。所以你会了吗&#xff1f;

寒假2.8

题解 web&#xff1a;[RoarCTF 2019]Easy Calc 打开&#xff0c;是一个计算界面 看一下源代码&#xff0c;提示设置了WAF&#xff0c;并且有一个calc.php文件 访问一下calc.php文件&#xff0c;得到源码&#xff0c;使用get方式传参赋值给num&#xff0c;设置了黑名单&#x…

表单与交互:HTML表单标签全面解析

目录 前言 一.HTML表单的基本结构 基本结构 示例 二.常用表单控件 文本输入框 选择控件 文件上传 按钮 综合案例 三.标签的作用 四.注意事项 前言 HTML&#xff08;超文本标记语言&#xff09;是构建网页的基础&#xff0c;其中表单&#xff08;<form>&…

UE5导入模型报错:多边形退化

解决方案&#xff1a;取消勾选Remove Degenerates

Leetcode—1474. 删除链表 M 个节点之后的 N 个节点【简单】Plus

2025每日刷题&#xff08;212&#xff09; Leetcode—1474. 删除链表 M 个节点之后的 N 个节点 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x…

OpenGL学习笔记(十二):初级光照:投光物/多光源(平行光、点光源、聚光)

文章目录 平行光点光源聚光多光源 现实世界中&#xff0c;我们有很多种类的光照&#xff0c;每种的表现都不同。将光投射(Cast)到物体的光源叫做投光物(Light Caster)。 平行光/定向光(Directional Light)点光源(Point Light)聚光(Spotlight) 平行光 当一个光源处于很远的地…

整合ES(Elasticsearch)+MQ(RabbitMQ)实现商品上下架/跨模块远程调用

商品上下架过程中&#xff0c;修改数据库表上下架状态&#xff0c;之后通过RabbitMQ发送消息&#xff0c;最终实现ES中数据同步 nacos服务发现和注册ES面向文档型数据库RabbitMQ ES 用户将数据提交到Elasticsearch数据库中通过分词控制器将对应的语句分词将其权重和分词结果一…

软件模拟I2C案例(寄存器实现)

引言 在经过前面对I2C基础知识的理解&#xff0c;对支持I2C通讯的EEPROM芯片M24C02的简单介绍以及涉及到的时序操作做了整理。接下来&#xff0c;我们就正式进入该案例的实现环节了。本次案例是基于寄存器开发方式通过软件模拟I2C通讯协议&#xff0c;然后去实现相关的需求。 阅…

Academy Sports + Outdoors EDI:体育零售巨头的供应链“中枢神经”

Academy Sports Outdoors 是美国领先的体育用品及户外装备零售商&#xff0c;拥有250线下门店及电商平台&#xff0c;年营收超60亿美元。作为全渠道零售商&#xff0c;其供应链面临独特挑战&#xff1a; 海量SKU管理&#xff1a;超50万SKU&#xff08;从健身器材到露营装备&a…

爬虫技巧汇总

一、UA大列表 USER_AGENT_LIST 是一个包含多个用户代理字符串的列表&#xff0c;用于模拟不同浏览器和设备的请求。以下是一些常见的用户代理字符串&#xff1a; USER_AGENT_LIST [Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; Hot Lingo 2.0),Mozilla…

信息学奥赛一本通1003

这道题题目中有一句“按每个整数占8个字符的宽度&#xff0c;右对齐输出它们”&#xff0c;诶&#xff0c;你会不会想到这个呢&#xff1a; #include<bits/stdc.h> using namespace std; int a, b, c; int main(){cin>> a>> b>> c;cout<<a&…

35~37.ppt

目录 35.张秘书-《会计行业中长期人才发展规划》 题目​ 解析 36.颐和园公园&#xff08;25张PPT) 题目​ 解析 37.颐和园公园&#xff08;22张PPT) 题目 解析 35.张秘书-《会计行业中长期人才发展规划》 题目 解析 插入自定义的幻灯片&#xff1a;新建幻灯片→重用…

【Android开发AI实战】基于CNN混合YOLOV实现多车牌颜色区分且针对车牌进行矫正识别(含源码)

文章目录 引言单层卷积神经网络&#xff08;Single-layer CNN&#xff09;&#x1f4cc; 单层 CNN 的基本结构&#x1f4cc; 单层 CNN 计算流程图像 透视变换矫正车牌c实现&#x1fa84;关键代码实现&#xff1a;&#x1fa84;crnn结构图 使用jni实现高级Android开发&#x1f3…

DeepSeek Window本地私有化部署

前言 最近大火的国产AI大模型Deepseek大家应该都不陌生。除了在手机上安装APP或通过官网在线体验&#xff0c;其实我们完全可以在Windows电脑上进行本地部署&#xff0c;从而带来更加便捷的使用体验。 之前也提到过&#xff0c;本地部署AI模型有很多好处&#xff0c;比如&…

R语言 文本分析 天龙八部

起因, 目的: 前面有人对 “倚天屠龙记” 进行分析,我这里只是进行模仿而已。 完整的文件, 已经绑定了,反正读者可以找一下。 案例背景 小说《天龙八部》是金庸先生所著的武侠小说,也是“射雕三部曲”的前传。全书共50章,字数超过一百万字。故事发生在北宋末年,以大理…

STM32G474--Whetstone程序移植(单精度)笔记

1 准备基本工程代码 参考这篇笔记从我的仓库中选择合适的基本工程&#xff0c;进行程序移植。这里我用的是stm32g474的基本工程。 使用git clone一个指定文件或者目录 2 移植程序 2.1 修改Whetstone.c 主要修改原本变量定义的类型&#xff0c;以及函数接口全部更换为单精度…

【专题】2024-2025人工智能代理深度剖析:GenAI 前沿、LangChain 现状及演进影响与发展趋势报告汇总PDF洞察(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p39630 在科技飞速发展的当下&#xff0c;人工智能代理正经历着深刻的变革&#xff0c;其能力演变已然成为重塑各行业格局的关键力量。从早期简单的规则执行&#xff0c;到如今复杂的自主决策与多智能体协作&#xff0c;人工智能代理…