SpringBoot分布式锁自定义注解处理幂等性

SpringBoot分布式锁+自定义注解处理幂等性

注解简介

注解(Annotation)是Java SE 5.0 版本开始引入的概念,它是对 Java 源代码的说明,是一种元数据(描述数据的数据)。

Java中的注解主要分为以下三类:

JDK的注解
第三方的注解
自定义注解

JDK注解

Java内置注解@Override (标记重写方法)@Deprecated (标记过时)@SuppressWarnings (忽略警告)
元注解 (注解的注解)@Target (注解的作用目标)@Retention (注解的生命周期)@Document (注解是否被包含在JavaDoc中)@Inherited (是否允许子类集成该注解)
@Target

用于描述注解的使用范围,有一个枚举ElementType来指定,具体如下:

Target类型描述
ElementType.TYPE应用于类、接口(包括注解类型)、枚举
ElementType.FIELD应用于属性(包括枚举中的常量)
ElementType.METHOD应用于方法
ElementType.PARAMETER应用于方法的形参
ElementType.CONSTRUCTOR应用于构造函数
ElementType.LOCAL_VARIABLE应用于局部变量
ElementType.ANNOTATION_TYPE应用于注解类型
ElementType.PACKAGE应用于包
ElementType.TYPE_PARAMETER应用于类型变量
ElementType.TYPE_USE应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)
@Retention

表示需要在什么级别保存该注释信息,用于描述注解的生命周期,也是一个枚举RetentionPoicy来决定的

取值含义
RetentionPolicy.SOURCE源码中保留,编译期可以处理
RetentionPolicy.CLASSClass文件中保留,Class加载时可以处理
RetentionPolicy.RUNTIME运行时保留,运行中可以处理

一般填RetentionPoicy.RUNTIME即可

@Documented

如果用javadoc生成文档时,想把注解也生成文档,就带这个。

@Inherited

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。注意,@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

自定义注解

使用JDK中一些元注解,@Target,@Retention,@Document,@Inherited来修饰注解。具体格式如下:
在这里插入图片描述

自定义注解实例:

import org.springblade.core.redis.lock.LockType;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;/*** 分布式锁注解处理幂等性* 分布式锁注解,Redisson,支持的锁的种类有很多,适合注解形式的只有重入锁、公平锁** <p>* 1. 可重入锁(Reentrant Lock)* 2. 公平锁(Fair Lock)* 3. 联锁(MultiLock)* 4. 红锁(RedLock)* 5. 读写锁(ReadWriteLock)* </p>*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisIdempotentLock {/*** 分布式锁的 key,必须:请保持唯一性** @return key*/String prefix() default "";/*** 分布式锁参数,可选,支持 spring el # 读取方法参数和 @ 读取 spring bean** @return param*/String param() default "";/*** 使用用户id作为新增等接口作为唯一key,处理幂等** @return param*/boolean isUserId() default false;/*** 等待锁超时时间,默认30** @return int*/long waitTime() default 30;/*** 自动解锁时间,自动解锁时间一定得大于方法执行时间,否则会导致锁提前释放,默认-1** @return int*/long leaseTime() default -1;/*** 时间单温,默认为秒** @return 时间单位*/TimeUnit timeUnit() default TimeUnit.SECONDS;/*** 默认公平锁** @return LockType*/LockType type() default LockType.FAIR;}

AOP 切面通用分布式锁+自定义注解处理幂等性;

import com.zhkj.ims.anotation.RedisIdempotentLock;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.lock.LockType;
import org.springblade.core.redis.lock.RedisLockClient;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.spel.BladeExpressionEvaluator;
import org.springblade.core.tool.utils.CharPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;/*** redis 分布式锁处理幂等性*/
@Aspect
@Component
public class RedisIdempotentLockAspect implements ApplicationContextAware {private static final Logger LOGGER = LoggerFactory.getLogger(RedisIdempotentLockAspect.class);/*** 表达式处理*/private static final BladeExpressionEvaluator EVALUATOR = new BladeExpressionEvaluator();@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate RedisLockClient redisLockClient;@Autowiredprivate ApplicationContext applicationContext;private static final String DEFAULT_SUPER_PREFIX = "idempotence";/*** AOP 环切 注解 @RedisIdempotentLock*/@Around(value = "@annotation(redisIdempotentLock)")public Object aroundRedisLock(ProceedingJoinPoint point, RedisIdempotentLock redisIdempotentLock) throws Throwable {String prefix = redisIdempotentLock.prefix();Class clazz = point.getTarget().getClass();String methodName = point.getSignature().getName();String lockName;String fullClassName = clazz.getName();if (StringUtils.hasText(fullClassName)) {String[] splits = fullClassName.split("\\.");String className = splits[splits.length - 1];lockName = className + CharPool.COLON + methodName;} else {lockName = methodName;}lockName = StringUtils.hasText(prefix) ? DEFAULT_SUPER_PREFIX + CharPool.COLON + prefix + CharPool.COLON + lockName : DEFAULT_SUPER_PREFIX + CharPool.COLON + lockName;String lockParam = redisIdempotentLock.param();String lockKey;if (StringUtil.isNotBlank(lockParam)) {// 解析表达式String evalAsText = evalLockParam(point, lockParam);lockKey = lockName + CharPool.COLON + evalAsText;if (redisIdempotentLock.isUserId()) {lockKey = lockKey + CharPool.COLON + AuthUtil.getUserId();}} else {if (redisIdempotentLock.isUserId()) {lockKey = lockName + CharPool.COLON + AuthUtil.getUserId();} else {lockKey = lockName;}}LockType lockType = redisIdempotentLock.type();long waitTime = redisIdempotentLock.waitTime();long leaseTime = redisIdempotentLock.leaseTime();TimeUnit timeUnit = redisIdempotentLock.timeUnit();Object result;boolean release = false;if (existKey(lockKey)) {throw new ServiceException("操作进行中,请稍后重试!");}try {boolean tryLock = redisLockClient.tryLock(lockKey, lockType, waitTime, leaseTime, timeUnit);if (tryLock) {release = true;result = point.proceed();} else {throw new ServiceException("操作进行中,请稍后重试!");}} catch (Exception e) {LOGGER.info("方法处理异常:{}", e.getMessage());throw e;} finally {if (release && existKey(lockKey)) {LOGGER.info("释放锁key:{}", lockKey);redisLockClient.unLock(lockKey, lockType);}}return result;}/*** 计算参数表达式** @param point     ProceedingJoinPoint* @param lockParam lockParam* @return 结果*/private String evalLockParam(ProceedingJoinPoint point, String lockParam) {MethodSignature ms = (MethodSignature) point.getSignature();Method method = ms.getMethod();Object[] args = point.getArgs();Object target = point.getTarget();Class<?> targetClass = target.getClass();EvaluationContext context = EVALUATOR.createContext(method, args, target, targetClass, applicationContext);AnnotatedElementKey elementKey = new AnnotatedElementKey(method, targetClass);return EVALUATOR.evalAsText(lockParam, elementKey, context);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}private boolean existKey(String lockKey) {Assert.hasText(lockKey, "lockKey must not null.");return redisTemplate.hasKey(lockKey);}
}

​ 具体使用

@RestController
public class TestController {@Autowiredprivate JdbcConfig jdbcConfig;@RedisIdempotentLock(param = "#id")@PostMapping("/hello")public String Hello(Long id) {return jdbcConfig.getUrl() + "  " + jdbcConfig.getDriver() + " " + jdbcConfig.getUser() + " " + jdbcConfig.getPassword();}
}

{

@Autowired
private JdbcConfig jdbcConfig;

@RedisIdempotentLock(param = “#id”)
@PostMapping(“/hello”)
public String Hello(Long id) {
return jdbcConfig.getUrl() + " " + jdbcConfig.getDriver() + " " + jdbcConfig.getUser() + " " + jdbcConfig.getPassword();
}
}

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

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

相关文章

【QT+QGIS跨平台编译】043:【protoc+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、protoc介绍二、文件下载三、文件分析四、pro文件五、编译实践一、protoc介绍 protoc 是 Protocol Buffers 的官方编译器 可执行文件版本。与在 Linux 或 macOS 等系统上的 protoc 类似,protoc.exe 在 Windows 环境下提供了相同的功能,用于将 .…

下拉选中搜索angularjs-dropdown-multiselect.js

需要引入angularjs-dropdown-multiselect.js 页面 <div ng-dropdown-multiselect"" options"supplierList_data" selected-model"supplierList_select" events"changSelValue_supplierList" extra-settings"mucommonsetti…

【MySQL】7.MHA高可用配置及故障切换

什么是MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件 mha用于解决mysql的单点故障问题&#xff1b; 出现故障时&#xff0c;mha能在0~30秒内自动完成故障切换&#xff1b; 并且能在故障切换过程中&#xff0…

Day54:WEB攻防-XSS跨站Cookie盗取表单劫持网络钓鱼溯源分析项目平台框架

目录 XSS跨站-攻击利用-凭据盗取 XSS跨站-攻击利用-数据提交 XSS跨站-攻击利用-flash钓鱼 XSS跨站-攻击利用-溯源综合 知识点&#xff1a; 1、XSS跨站-攻击利用-凭据盗取 2、XSS跨站-攻击利用-数据提交 3、XSS跨站-攻击利用-网络钓鱼 4、XSS跨站-攻击利用-溯源综合 漏洞原理…

【群晖】白群晖如何公网访问

【群晖】白群晖如何公网访问 ——> 点击查看原文 在使用默认配置搭建好的群晖NAS后&#xff0c;我们可以通过内网访问所有的服务。但是&#xff0c;当我们出差或者不在家的时候也想要使用应该怎么办呢&#xff1f; 目前白群提供了两种比较快捷的方式&#xff0c;一种是直接注…

Linux 环境安装 Elasticsearch 8.X

安装前说明 首先确定操作系统&#xff0c;在Linux发行版上执行uname -a查看具体系统。我是Ubuntu系统&#xff0c;可以用直接用apt-get安装&#xff0c;也可以下载tar.gz包手动安装。使用apt-get安装更方便快速&#xff0c;但不同的文件会被安装到不同的目录&#xff0c;不方便…

XUbuntu22.04之Typora快捷键Ctrl+5不生效问题(二百二十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

在线随机密码生成器源码

纯HTML&#xff0c;该去的已去掉&#xff0c;该简化的简化&#xff0c;最高支持32位混合随机密码生成。 源码下载&#xff1a;在线随机密码生成器源码

多视图三维重建-SFM简介

背景 掌握传统的多视图三维重建基本流程 总体流程 多视图三维重建的Pipieline如下图&#xff0c;总共分为四个步骤&#xff1a; 拍摄场景多视角的图像建立这些图像之间的联系&#xff08;Data Association&#xff09;SFM稀疏重建MVS稠密重建 Data Association 建立图像…

独立游戏《星尘异变》UE5 C++程序开发日志3——UEC++特供的数据类型

本篇日志将介绍FString&#xff0c;FText、FName的用法和相互转换&#xff0c;以及容器TMap&#xff0c;TArray的增删查改 一、字符串相关数据类型&#xff1a;FString、FText、FName FString是最接近std::string的类型&#xff0c;字符串本身可以看做一个存储char型的动态数…

【echart】数据可视化

什么是数据可视化&#xff1f; 数据可视化主要目的:借助于图形化手段&#xff0c;清晰有效地传达与沟通信息。 数据可视化可以把数据从冰冷的数字转换成图形&#xff0c;揭示蕴含在数据中的规律和道理。 如何绘制&#xff1f; echarts 图表的绘制&#xff0c;大体分为三步:…

跃然纸上的灵感再现,手绘风格的开源绘图白板工具:Excalidraw

Excalidraw&#xff1a;即绘即思&#xff0c;直观呈现未来流程图&#xff01;- 精选真开源&#xff0c;释放新价值。 概览 在撰写文章或构建演示案例的过程中&#xff0c;为了增强视觉表现力和信息传达深度&#xff0c;适时融入图表或图形显得至关重要。Excalidraw作为一款基于…

GT收发器第四篇_QPLL和CPLL工作原理

文章目录 前言一、CPLL工作原理二、QPLL工作原理 前言 每个channel的时钟结构如图&#xff1a; Transceiver内部时钟来源可以是QPLL也可以是自己的CPLL。其内部TX 和 RX 时钟分频器可以单独从 QPLL 或 CPLL 中选择时钟&#xff0c;允许 TX和 RX 数据通道使用不同的参考时钟输入…

AXS2003B 爱协生 2.4W单通道 AB类音频功率放大器 兼容LM4890 低成本

AXS2003B 是一颗单通道AB类音频功率放大器。在5V电源供电&#xff0c;THDN10%&#xff0c;4欧姆负载上可以输出2.4W 的功率。 AXS2003B优异的噪声和THD指标可以提供高品质的音频信号放大。极少的外围元件就能提供芯片稳定工作&#xff0c;大大减少了PCB面积并降低成本。 AXS20…

蓝桥备赛——堆队列

AC code import os import sys import heapq a [] b [] n,k map(int,input().split())for _ in range(n):x,y map(int,input().split())a.append(x)b.append(y) q []# 第一种情况&#xff1a;不打第n个怪兽# 将前n-1个第一次所需能量加入堆 for i in range(n-1):heapq.h…

【edge浏览器无法登录某些网站,以及迅雷插件无法生效的解决办法】

edge浏览器无法登录某些网站&#xff0c;以及迅雷插件无法生效的解决办法 edge浏览器无法登录某些网站&#xff0c;但chrome浏览器可以登录浏览器插件无法使用&#xff0c;比如迅雷如果重装插件重装浏览器重装迅雷后仍然出现问题 edge浏览器无法登录某些网站&#xff0c;但chro…

网站业务对接DDoS高防

准备需要接入的网站域名清单&#xff0c;包含网站的源站服务器IP&#xff08;仅支持公网IP的防护&#xff09;、端口信息等。所接入的网站域名必须已完成ICP备案。如果您的网站支持HTTPS协议访问&#xff0c;您需要准备相应的证书和私钥信息&#xff0c;一般包含格式为.crt的公…

linux 组建raid5详细操作

raid5最多运行损坏一个盘&#xff0c;最少3个盘&#xff0c;容量为少一块硬盘的容量之和。 如果硬盘数量较多&#xff0c;比如8块以上&#xff0c;建议用raid6&#xff0c;raid6最多允许两块硬盘损坏。 如果需要 一、安装raid软件 deb包 apt-get install mdadm或dnf包 dnf …

Servlet基础 管理员注册页面

管理员注册页面 index.jsp <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% String path request.getContextPath(); String basePath request.getScheme()"://"request.getServerName()":&quo…

小程序UI设计规范,界面设计尺寸详解

作为互联网技术的重要组成部分&#xff0c;小程序在日常生活中发挥着越来越重要的作用。因此&#xff0c;了解和严格遵守小程序的 UI 设计标准非常重要&#xff0c;它不仅可以帮助我们在保证良好用户体验的同时优化小程序&#xff0c;还可以使我们的产品在竞争激烈的市场中占据…