二言目 - Java
type: channel
link: https://blog.piemoe.com/category/Java/
items: Mac常用Java开发环境 docker-compose版
items: Swagger 枚举字段显示在接口文档中，并自定义枚举返回
items: 常用的@Superwarnning 注解大全
items: 使用SpringCloud Alibaba结合Springboot3.0 搭建一个demo
items: 使用Optional增强Mybatis-plus体验
items: 通过注解或Lambda方式进行数据脱敏
items: 制作使用mqtt发送消息的Spring starter
items: Springboot通过注解实现敏感词过滤
items: 支持IDEA的Java规范代码格式化配置文件
items: 使用Lock4j完成一个分布式锁和幂等的Spring Boot Starter组件

Mac常用Java开发环境 docker-compose版
type: item
date: 2023-04-12T15:21:00+08:00
description: version: '3.2' services: redis5: image: redis:5 container_name: redis5 restart: always environment: - ALLOW_EMPTY_PASSWORD=yes command: redis-server --appendonly yes --requirepass 123456 volumes: - redis:/data #数据文件挂载 ports: - 6379:6379 nacos-registry: image: nacos/nacos-server:v2.2.3 container_name: nacos environment: - PREFER_HOST_MODE=hostname - MODE=standalone - SPRING_DATASOURCE_PLATFORM=mysql - MYSQL_SERVICE_HOST=mysql - MYSQL_SERVICE_DB_NAME=nacos - MYSQL_SERVICE_PORT=3306 - MYSQL_SERVICE_USER=root - MYSQL_SERVICE_PASSWORD=123456 - MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true # volumes: # - /docker/service/env/nacos:/home/nacos/init.d ports: - "8848:8848" - "9555:9555" - "9848:9848" - "9849:9849" restart: on-failure external_links: - mysql:db #可以用db这个域名访问mysql服务 depends_on: - mysql mysql: image: mysql container_name: mysql command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --lower_case_table_names=1 --log_bin_trust_function_creators=1 --max_allowed_packet=100G --tmp_table_size=256M --key_buffer_size=512M --innodb_log_file_size=1024M restart: always environment: MYSQL_ROOT_PASSWORD: 123456 #设置root帐号密码 ports: - 3306:3306 volumes: - mysql8:/var/lib/mysql #数据文件挂载 volumes: redis: external: true mysql8: external: true

Swagger 枚举字段显示在接口文档中，并自定义枚举返回
type: item
date: 2023-03-01T12:17:00+08:00
description: Swagger 枚举字段显示在接口文档中，并自定义枚举返回最近使用枚举做为接收参数，接收枚举中的某一个属性，返回对应的枚举，但是发现这种方法不能在swagger文档中体现，故此拓展springfox插件来实现。⚠️ 本次修改兼容 v2 v3先来看下效果：请求参数显示枚举信息响应显示枚举信息接下来实操请求参数和响应显示枚举信息创建枚举对应显示信息注解其中 value 为入参数值，desc 为入参的描述@Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface EnableSwaggerEnum { String value() default "value"; String desc() default "desc"; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface EnableSwaggerResponseEnum { String value() default "value"; String desc() default "desc"; }创建一个公共修改model由于要兼容v2 和 v3,故此需要创建一个修改的公共model，用来存放修改的枚举信息。其中，displayValues是用来显示各个枚举值和信息的字符串，allowableListValues 为实际要显示的入场值。@Data public class EnumMidModel { private String displayValues; private AllowableListValues allowableListValues; }封装modelpublic class SwaggerEnumUtils { /** * 封装枚举消息 * * @param type 此解析类型具有的类型擦除 Class<?> * @param enableSwaggerEnum 自定义swagger枚举限制注释 {@link EnableSwaggerEnum} * @return {@link EnumMidModel} * @author Mc * @since 2023/3/1 20:24 */ protected static EnumMidModel getEnumMidModel(Class<?> type, EnableSwaggerEnum enableSwaggerEnum) { EnumMidModel enumMidModel = new EnumMidModel(); String value = enableSwaggerEnum.value(); String desc = enableSwaggerEnum.desc(); Object[] enumConstants = type.getEnumConstants(); // 拼装显示内容 final String displayValues = Arrays.stream(enumConstants) .filter(Objects::nonNull) .map( item -> { Object valueVal = ReflectUtil.getFieldValue(item, value); Object descVal = ReflectUtil.getFieldValue(item, desc); return valueVal.toString() + " : " + descVal.toString(); } ) .collect(Collectors.joining("】\n【", "\n【", "】")); List<String> allowableValue = Arrays.stream(enumConstants) .filter(Objects::nonNull) .map(item -> ReflectUtil.getFieldValue(item, value).toString()) .collect(Collectors.toList()); AllowableListValues allowableListValues = new AllowableListValues(allowableValue, "string"); enumMidModel.setDisplayValues(displayValues); enumMidModel.setAllowableListValues(allowableListValues);…

常用的@Superwarnning 注解大全
type: item
date: 2023-02-25T13:34:00+08:00
description: 常用的@Superwarnning 注解大全​ 使用idea的时候，有时避免不了会被编辑器提示警告，虽然不影响代码和程序的正常观看和运行，但总之是不美观，这时便可以用@Superwarnning注解来抑制警告来。尽量在代码编辑无法穷善的时候使用@SuppressWarnings("unchecked")//执行了未检查的转换时的警告，例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。 @SuppressWarnings("unused") //未使用的变量 @SuppressWarnings("resource") //有泛型未指定类型 @SuppressWarnings("path") //在类路径、源文件路径等中有不存在的路径时的警告 @SuppressWarnings("deprecation") //使用了不赞成使用的类或方法时的警告 @SuppressWarnings("fallthrough") //当 Switch 程序块直接通往下一种情况而没有 break; 时的警告 @SuppressWarnings("serial")//某类实现Serializable(序列化)， 但没有定义 serialVersionUID 时的警告 @SuppressWarnings("rawtypes") //没有传递带有泛型的参数 @SuppressWarnings("finally") //任何 finally 子句不能正常完成时的警告。 @SuppressWarnings("try") // 没有catch时的警告 @SuppressWarnings("DuplicatedCode") //重复代码警告抑制 @SuppressWarnings("SpellCheckingInspection") // 抑制错误拼写 @SuppressWarnings("DuplicatedCode") // 抑制重复代码提示 @SuppressWarnings("all") //所有类型的警告 @SuppressWarnings("UnusedParameters") // 未使用的参数 // 阿里巴巴插件 @SuppressWarnings("AlibabaClassNamingShouldBeCamel") //【xXxXXxX】不符合UpperCamelCase命名风格 @SuppressWarnings（"AlibabaLowerCamelCaseVariableNaming") // 变量名【xxXX】不符合lowerCamelCase命名风格

使用SpringCloud Alibaba结合Springboot3.0 搭建一个demo
type: item
date: 2023-02-17T12:51:00+08:00
description: Springboot 3 出来也有一段时间了，Springcloud alibaba也在适配当中，本demo使用Springboot 3 + Spingcloud alibaba + nacos等组件，完成一个项目工程的简单搭建组件Nacos : 2.0.3Maven：3.0.8依赖版本springboot :3.0.4springcloud:2022.0.1springcloud alibaba :2.2.10-RC1spring-cloud-starter-alibaba-nacos-discovery:2022.0.0.0-RC1spring-cloud-starter-alibaba-nacos-config：2022.0.0.0-RC1springcloud alibaba 2.2.10-RC1 版本提供的Nacos依赖，经测试无法成功注册，故使用2022.0.0.0-RC1版本项目POM<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.4</version> <relativePath/> </parent> <groupId>com.piemoe</groupId> <artifactId>springboot3</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot3</name> <description>springboot3</description> <properties> <java.version>17</java.version> </properties> ​ <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2022.0.1</version> <type>pom</type> <scope>import</scope> </dependency> ​ <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.10-RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ​ <dependencies> ​ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dep…

使用Optional增强Mybatis-plus体验
type: item
date: 2023-01-04T18:11:00+08:00
description: MyBatis-PlusMyBatis-Plus（简称MP）是MyBatis社区的一个增强工具包，它在MyBatis的基础上进行了扩展，封装了很多有用的功能，提高了开发效率。MyBatis-Plus中包含了许多常用的增删改查的方法，还有分页查询、条件构造器、乐观锁、多租户、性能分析等功能。以下是MyBatis-Plus的一些特点：简化开发：MyBatis-Plus可以通过代码自动生成器快速生成CRUD方法的代码，极大地提高了开发效率。增删改查方法：MyBatis-Plus封装了通用的增删改查方法，无需手写SQL语句。集成分页插件：MyBatis-Plus集成了分页插件，使用简单，可以轻松实现分页操作。条件构造器：MyBatis-Plus提供了条件构造器，可以通过代码的形式构造复杂的SQL查询语句。乐观锁：MyBatis-Plus提供了乐观锁的功能，避免了并发操作时的数据冲突。多租户：MyBatis-Plus支持多租户，可以通过配置实现多租户系统的开发。性能分析：MyBatis-Plus集成了性能分析插件，可以分析SQL查询的性能瓶颈，帮助优化SQL语句，提高查询效率。总之，MyBatis-Plus是一个非常优秀的MyBatis增强工具包，其功能强大且易于使用，在实际开发中也能够显著地提高开发效率和工作效率。Java OptionalJava Optional是Java 8中引入的一个类，用于表示一个值可能存在也可能不存在。它的主要目的是减少空指针异常。Java Optional类有两个重要的方法:isPresent()和get()。isPresent()方法用于检查值是否存在，get()方法用于获取值。如果值不存在，get()方法会抛出NoSuchElementException异常。Java Optional还有很多有用的方法，比如orElse()和orElseGet()方法。这些方法可以帮助我们处理值不存在的情况。Java Optional是一个泛型类，因此可以用于处理任何类型的值。使用Java Optional的主要优点是可以避免空指针异常，提高代码健壮性。堵点MyBatis-Plus提供了很好的CURD体验，大大加强了开发的效率，其Lambda表达式式的方式也大大增强了用户的体验，但是MyBatis-Plus返回的参数仍然会在实际项目中被忽略空指针问题，笔者的朋友和同事就经常因为空指针问题（主要是忘记）经常被测试吐槽，故此通过可以使用Optional来增强Mybatis-plus的体验，一定程度上可以避免“忘记”判空带来的空指针问题。代码项目中引入MyBatis-Plus，新建OptionalBaseMapper文件，实现BaseMapper，具体代码如下：@SuppressWarnings({"DuplicatedCode", "unused"}) public interface OptionalBaseMapper<T> extends BaseMapper<T> { // ======================================= 批量插入 ======================================== /** * 批量插入 仅适用于mysql * * @param entityList 实体列表 * @return 影响行数 */ Integer insertBatchSomeColumn(Collection<T> entityList); /** * 逐条插入，适合少量数据插入，或者对性能要求不高的场景 * * 如果大量，请使用 {@link com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch(Collection)} 方法 * 使用示例，可见 RoleMenuBatchInsertMapper、UserRoleBatchInsertMapper 类 * * @param entities 实体们 */ default void insertBatch(Collection<T> entities) { entities.forEach(this::insert); } // ======================================= 查询数量 ======================================== /** * 根据 Wrapper 条件，查询总记录数 * * @param queryWrapper 实体对象封装操作类（可以为 null） */ default Optional<Long> getCount(Wrapper<T> que…

通过注解或Lambda方式进行数据脱敏
type: item
date: 2022-11-10T10:51:00+08:00
description: 前言数据脱敏是一种数据保护技术，它可以对敏感数据进行处理，使其失去原始意义，从而保护数据隐私。通俗地说，就是将原始数据中敏感信息的部分替换或删除，例如：手机号码中的一部分数字替换为星号，或是姓名中的一些字母用其他字母代替等等。数据脱敏可以保护个人隐私，防止个人信息被泄露、滥用，同时也符合现代法律法规对个人信息保护的要求，是网络安全和信息保护中不可或缺的一环。本文将通过使用注解完成对字段的自定义脱敏功能。正文本文将介绍2种脱敏的方式，分别为：注解方式脱敏和lambda方式脱敏注解脱敏定义脱敏注解@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @JacksonAnnotationsInside @JsonSerialize(using = SensitiveSerialize.class) public @interface Dm { /** * 脱敏信息类型 */ DmTypeEnum type(); /** * 前置不需要打码的长度 * 只有 {@link DmTypeEnum} 为 {@link DmTypeEnum#CUSTOMER} 时可用 */ int prefixNoMaskLen() default 0; /** * 后置不需要打码的长度 * 只有 {@link DmTypeEnum} 为 {@link DmTypeEnum#CUSTOMER} 时可用 */ int suffixNoMaskLen() default 0; /** * 用什么打码 * 只有 {@link DmTypeEnum} 为 {@link DmTypeEnum#CUSTOMER} 时可用 */ char symbol() default '*'; }type()方法为可以支持实现的脱敏信息类型，通过使用hutool工具，可以看到它支持以下枚举，故拉取出来形成类型枚举：public enum DmTypeEnum { /** * 用户自定义 */ CUSTOMER, /** * 用户id */ BACK_ID, /** * 中文名 */ CHINESE_NAME, /** * 身份证号 */ ID_CARD, /** * 座机号 */ FIXED_PHONE, /** * 手机号 */ MOBILE_PHONE, /** * 地址 */ ADDRESS, /** * 电子邮件 */ EMAIL, /** * 密码 */ PASSWORD, /** * 中国大陆车牌，包含普通车辆、新能源车辆 */ CAR_LICENSE, /** * 银行卡 */ BANK_CARD }关键语句在@JsonSerialize(using = SensitiveSerialize.class)上，@JsonSerialize 是一个注解，它可以用来指定 Java 对象序列化成 JSON 格式时的序列化规则。具体而言，它可以指定序列化时属性名的命名方式、日期格式化等等。它的作用是使得 Java 对象可以方便地转化为 JSON 格式，这对于 Web 开发、移动应用开发等场景非常有用，故此我们在这里自定义一个实现，并在注解中引用。@NoArgsConstructor @AllArgsConstructor @Slf4j public class SensitiveSerialize<T> extends JsonSerializer<T> implements ContextualSerializer { /** * 脱敏类型 */ private DmTypeEnum sensitiveTypeEnum; /** * 前几位不脱敏 */ private Integer prefixNoMaskLen; /** * 最后几位不脱敏 */ private Integer suffixNoMaskLen; /** * 打码字符 */ private char symbol; @Override public void serialize(final T value, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException { Object desensitized = DmUtils.desensitized(value.toString(), sensitiveTypeEnum, prefixNoMaskLen, suffixNoMaskLen, symbol); if (desensitized instanceof String) { jsonGene…

制作使用mqtt发送消息的Spring starter
type: item
date: 2022-06-07T15:29:00+08:00
description: 使用mqtt发送消息Spring starter最近有个项目需要发送视频弹幕，实现的手段还是不少的，这里用Mqtt来实现。准备首先是环境准备，这里使用 EMQX 5.0 社区版作为Mqtt的消息服务器，使用Docker进行搭建：version: '3.2' services: emqx: image: emqx/emqx:v5.0.0 container_name: emqx environment: EMQX_NAME: xinwei-emqx restart: always ports: - '1883:1883' - '8081:8081' - '8083:8083' - '8883:8883' - '8084:8084' - '8099:8080' - '18083:18083' volumes: - /service/env/emqx/data:/opt/emqx/data - /service/env/emqx/etc:/opt/emqx/etc - /service/env/emqx/log:/opt/emqx/log正文Maven具体版本可以由项目的spring-boot版本决定。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-integration</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-mqtt</artifactId> </dependency>配置配置类本文不对mqtt进行鉴权，需要的可以自行拓展@Data @ConfigurationProperties(prefix = "spring.mqtt") @RefreshScope public class MqttProperties { private String url; private AuthTypeEnum authType = AuthTypeEnum.NONE; private String username; private String password; private String wsUrl; private String client; private Topic defaultSendTopic = new Topic("xinwei_mqtt_default", 1); private List<Topic> receiveTopics; private Integer keepAlive = 15; private Integer completionTimeout = 3000; /** * Mqtt 权限认证类型枚举 */ @NoArgsConstructor public enum AuthTypeEnum { /** * 无 */ NONE, /** * 客户端id */ CLIENT_ID, /** * 用户名 */ USERNAME } @Data @NoArgsConstructor @AllArgsConstructor public static class Topic { private String name; private Integer qos; } }注入配置消息发送@AutoConfiguration @EnableConfigurationProperties(MqttProperties.class) public class MqttConfiguration { @Bean public MessageChannel mqttValueInputChannel() { return new DirectChannel(); } /** * 不缓存消息 * * @return 消息通道 */ @Bean public MessageChannel mqttOutputClearSessionChannel() { return new DirectChannel(); } /** * 缓存消息 * * @return 消息通道 */ @Bean public MessageChannel mqttOu…

Springboot通过注解实现敏感词过滤
type: item
date: 2022-05-11T15:59:00+08:00
description: 前言项目用到了敏感词过滤（数据清洗），之前是通过查询敏感词库进行模糊匹配然后过滤的，但这样的查表的效率很低，且跨微服务调用时时间损耗会更高，故此记录改造正文首先想到的是将敏感词从库表里放入redis缓存，这样可以有效避免重复查库，其次通过redis可以较少微服务调用，直接从redis缓存使用。但是一段语句中，是否存在敏感词，直接匹配redis缓存中的敏感词库的话，效率依旧不高，故此我们可以引入知名的DFA算法。DFA算法介绍DFA(确定有限状态自动机) 算法是一种用于解决文本匹配问题的算法。它通过比较输入文本和预定义的状态机的状态转换路径，来判断是否存在匹配。DFA 通常用于字符串匹配。它利用预先创建好的有限状态自动机，自动机包含一个有限的状态集合和一个输入符号集合，自动机从一个状态转移到另一个状态，根据输入符号，自动机判断出输入符号是否与预定义的字符串匹配。 其主要思想是将匹配过程建模为状态机，并使用状态机的转移过程进行匹配。下面是一些使用 DFA 的例子：字符串匹配：在一个输入文本中查找一个特定的字符串是否存在。如果它存在，就返回它在文本中的位置。如果不存在，则返回-1。词法分析：将输入的字符流识别成包含令牌、标记或关键字的单词流。例如，在编译器中，它用于将代码解析成令牌流。自然语言处理：用于命名实体识别等自然语言处理任务。在这些任务中，它可以使用 DFA 对文本进行分词，并构建一个有限状态自动机，以识别关键字或语法结构。过滤器：用于过滤输入文本中的某些内容。在这些任务中，它可以使用 DFA 对文本进行匹配和替换，以过滤或修改输入文本中的内容。更多的介绍可以看这篇文章：基于DFA敏感词查询的算法简析 - 李晓晖 - 博客园 (cnblogs.com)DFA实现站在巨人的肩膀上才能看的更高，故此直接使用hutools工具的DFA算法！DFA查找 (hutool.cn)敏感词过滤实现本人根据自己日常编码的使用需求，大致分为2种使用场景：方法内过滤这个很好理解，就是方法体内进行过滤，这个直接使用hutool提供的方法结合redis使用即可，不再赘述。方法调用时过滤作为入参参数过滤，即方法调用时过滤根据2个场景又可以分成2种风格：遇到敏感词即禁止（截止模式）替换敏感词为其他符号（置换模式）敏感词首先定义一个敏感词库，使用hash的方式将其存入redis，这部分实现很简单，不再赘述。截止模式此模式为遇到敏感词即报错提示，通过统一异常管理可以快捷通知用户内容包含敏感词首先定义一个注解：/** * 自定义敏感词过滤注解 * 若包含敏感词，则返回false 不替换敏感词 效率更高 * @author mc */ @Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = SwfValidator.class) public @interface SwfParam { String message() default "内容含有敏感词汇，请检查！"; }这个注解可以在方法、字段、构造器和入参字段字段使用，message为报错的信息，@Constraint(validatedBy = SwfValidator.class)为检验方法，是具体的过滤方法。/** * 自定义敏感词校验注解实现 * * @author mc */ public class SwfValidator implements ConstraintValidator<SwfParam, String> { @Resource ISwfService swfService; @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { return containsKeyWord(value); } public boolean containsKeyWord(String value) { WordTree sensitiveWordTree = new WordTree(); Set<String> sensitiveWordSet = swfService.getRemoteSensitiveWordMap(true); sensitiveWordTree.addWords(sensitiveWordSet); String ma…

支持IDEA的Java规范代码格式化配置文件
type: item
date: 2022-05-04T10:58:00+08:00
description: 支持IDEA的Java规范代码格式化配置文件详情参阅：编辑器配置 (editorconfig.org)更多配置详情参阅：EditorConfig Properties · editorconfig/editorconfig Wiki (github.com)配置详解：在项目根目录下创建 .editorconfig文件，加入如下内容：# http://editorconfig.org root = true # 空格替代Tab缩进在各种编辑工具下效果一致 [*] indent_style = space indent_size = 4 charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true [*.java] ########################## idea java doc 格式化设置 start ########################## # 是否开启doc注释格式化 ij_java_doc_enable_formatting = true # 是否对齐doc参数注释 ij_java_doc_align_param_comments = true # 是否对齐异常注释 ij_java_doc_align_exception_comments = true # 是否保留空参注释 ij_java_doc_keep_empty_parameter_tag = false # 是否保留空返回注释 ij_java_doc_keep_empty_return_tag = false # 是否保留空抛错注释 ij_java_doc_keep_empty_throws_tag = false # 是否将空行加入 标签 ij_java_doc_add_p_tag_on_empty_lines = true # 是否在参数说明后面加入空行 ij_java_doc_add_blank_line_after_param_comments = true # 是否在方法说明后加入空行 ij_java_doc_add_blank_line_after_description = true # 是否使用@throw而不是@exception ij_java_doc_use_throws_not_exception_tag = true ########################## idea java doc 格式化设置 end ########################## ########################## idea java 空格 格式化设置 start ########################## # 括号之前 ij_java_space_before_for_parentheses = true ij_java_space_before_if_parentheses = true ij_java_space_before_while_parentheses = true ij_java_space_before_switch_parentheses = true ij_java_space_before_try_parentheses = true ij_java_space_before_catch_parentheses = true ij_java_space_before_synchronized_parentheses = true # 左大括号之前 ij_java_space_before_class_left_brace = true ij_java_space_before_method_left_brace = true ij_java_space_before_for_left_brace = true ij_java_space_before_if_left_brace = true ij_java_space_before_else_left_brace = true ij_java_space_before_do_left_brace = true ij_java_space_before_while_left_brace = true ij_java_space_before_switch_left_brace = true ij_java_space_before_try_left_brace = true ij_java_space_before_catch_left_brace = true ij_java_space_bef…

使用Lock4j完成一个分布式锁和幂等的Spring Boot Starter组件
type: item
date: 2022-04-24T12:35:00+08:00
description: 前言分布式锁​ 分布式锁是在分布式系统中保证同步和协调的一种机制。它通常用于协调多个进程或节点之间的访问和资源使用，以防止并发冲突和数据损坏。​ 分布式锁的实现通常采用一种称为互斥体的同步原语。互斥体是一种锁定机制，可以用来确保一个进程或节点在任何时候都具有对某个资源的独占访问权限。在分布式环境中，互斥体必须经过适当的设计和实现，以确保它能够正确地工作和协调不同的节点和进程。​ 常见的分布式锁实现方式包括：基于Redis的分布式锁、ZooKeeper的分布式锁、数据库乐观锁等。这些实现方式中，Redis和ZooKeeper常常被使用在大规模的分布式系统中，因为它们提供了可靠的数据存储和高效的访问控制机制。而数据库乐观锁则主要用于小规模分布式系统中，因为它比较简单直接，但是更加依赖于网络的性能。幂等​ 幂等是指对同一操作，多次执行所产生的影响与执行一次的影响相同。也就是说，即使同一操作被执行多次，也不会对系统产生额外的影响。在计算机科学中，幂等性是一种非常重要的概念，常常用于设计和实现高可靠性、可复用性、可扩展性的分布式系统和网络服务。常见的幂等操作包括查询数据库、修改状态、发送邮件等。选型[scode type='info']本次使用lock4j+redisson实现分布式锁和幂等组件，其中，redisson的版本不应该过高，要和Spring Boot版本对应，否则无法正常启动。[/scode]Spring Boot：2.6lock4j: 2.2.3官方仓库：lock4j: 基于Spring AOP 的声明式和编程式分布式锁，支持RedisTemplate、Redisson、Zookeeper (gitee.com)正文首先搭建一个Spring Boot 2.6.3 的项目，搭建过程省略。引入lock4j+redisson依赖<dependency> <groupId>com.baomidou</groupId> <artifactId>lock4j-redisson-spring-boot-starter</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>分布式锁首先对lock4j进行一些配置，默认的lock4j在缓存中是以lock4j为前缀的key，但是为了保持项目的一致（比如某个微服务就以它的项目名称为开头）就需要修改其默认的key前缀。@AutoConfigureBefore(LockAutoConfiguration.class) public class MyLock4jConfig { // 引入默认的配置 private final Lock4jProperties properties; // 修改lock4j前缀 自定义默认使用key 项目名称:lock4j:*** public MyLock4jConfig(Lock4jProperties properties, ApplicationName applicationName) { properties.setLockKeyPrefix(applicationName.getServiceName() + ":lock4j"); this.properties = properties; } @Bean @ConditionalOnMissingBean @SuppressWarnings("all") // 这里加入 @ConditionalOnMissingBean注解，目的是为了项目代码里可以灵活重写 // @SuppressWarnings("all")可以抑制idea的警告 public LockTemplate lockTemplate(List<LockExecutor> executors) { LockTemplate lockTemplate = new LockTemplate(); lockTemplate.setProperties(properties); lockTemplate.setExecutors(executors); return lockTemplate; } // 默认的锁失败策略 这里进行自定义 @Bean public DefaultLockFailureStrategy lockFailureStrategy() { return new D…