ContiNew

幂等

continew-starter-idempotent 基于 AOP + 注解结合 Redis 实现的分布式幂等模块,通过 @Idempotent 注解一行代码防止接口重复提交。

continew-starter-idempotent 基于 AOP + 注解结合 Redis 实现分布式幂等,在接口方法上添加 @Idempotent 注解即可防止重复提交,支持 SpEL 表达式动态解析幂等 Key。

主要特性:

  • 开箱即用:引入依赖后仅需一个注解即可生效
  • 支持 SpEL 表达式:可动态解析请求参数作为幂等 Key,如 @Idempotent(key = "#req.username")
  • 提供默认的幂等名称生成器 DefaultIdempotentNameGenerator,也可自行实现替换

引入依赖

<dependency>
    <groupId>top.continew.starter</groupId>
    <artifactId>continew-starter-idempotent</artifactId>
</dependency>

本模块内部依赖 continew-starter-cache-redisson 进行幂等键的存取(见 pom.xml 第 19-23 行),因此需要可用的 Redis 环境。

配置

配置属性类为 top.continew.starter.idempotent.autoconfigure.IdempotentProperties。

continew-starter:
  idempotent:
    # 是否启用(默认:true)
    enabled: true
    # 幂等统一 Key 前缀(默认:Idempotent)
    key-prefix: Idempotent
配置项说明默认值
continew-starter.idempotent.enabled模块开关(自动配置条件,非绑定属性;matchIfMissing = true,不配即启用)true
continew-starter.idempotent.key-prefix幂等键在 Redis 中的统一前缀Idempotent

enabled 是自动配置类上的 @ConditionalOnProperty 条件,不由 IdempotentProperties 绑定,因此 IDE 配置提示里不会出现这一项。设置它照样生效(关掉整个模块),只是它和下面这类有 getter/setter 的键在机制上不同。

使用

在需要幂等处理的接口方法上添加 top.continew.starter.idempotent.annotation.Idempotent 注解:

@Operation(summary = "创建用户")
@PostMapping
@Idempotent(key = "#req.username")
public R createUser(@Valid @RequestBody UserReq req) {
    // 业务逻辑 ...
}

在 timeout 时间窗口内,相同幂等键的重复请求会抛出 IdempotentException,默认提示「请勿重复操作」。

@Idempotent 注解属性

源码见 Idempotent.java,各属性及默认值如下:

属性类型默认值说明
nameString""幂等名称;留空时由 IdempotentNameGenerator 自动生成
keyString""幂等键,支持 SpEL 表达式,按方法参数动态解析
timeoutint1000超时时间(时间窗口),窗口内重复请求视为重复提交
unitTimeUnitTimeUnit.MILLISECONDS超时时间单位
messageString"请勿重复操作"重复提交时的提示信息

幂等键的生成规则

幂等键由 IdempotentAspect.getCacheKey 生成,最终格式为 keyPrefix:name:key,各部分用 : 拼接(空白部分会被忽略):

  • keyPrefix:即配置项 continew-starter.idempotent.key-prefix,默认 Idempotent;
  • name:注解 name 属性;为空时由 IdempotentNameGenerator 生成。默认实现 DefaultIdempotentNameGenerator 按「类名:方法名:参数 JSON 的 MD5」生成;
  • key:注解 key 属性的 SpEL 解析结果,如 #req.username;若解析结果为 null 会抛出 IdempotentException("幂等 Key 解析错误")。

注意:目标方法执行抛出异常时,切面会主动删除该幂等键(见 IdempotentAspect.around 第 73-77 行),以便调用方重试;只有执行成功的请求才会在 timeout 窗口内拦截重复提交。

自定义幂等名称生成器

实际业务中,幂等通常需要区分当前用户(如拼接当前用户的 Token、用户 ID)。此时可实现 IdempotentNameGenerator 接口并注册为 Spring Bean,自动配置的默认生成器会被替换(@ConditionalOnMissingBean):

@Configuration
public class GlobalConfiguration {

    /**
     * 自定义幂等名称生成器
     */
    @Bean
    public IdempotentNameGenerator idempotentNameGenerator() {
        return new MyIdempotentNameGenerator();
    }
}
public class MyIdempotentNameGenerator implements IdempotentNameGenerator {

    @Override
    public String generate(Object target, Method method, Object... args) {
        // 拼接当前用户标识 + 方法签名,实现按用户隔离的幂等
        return LoginHelper.getUserId() + ":" + target.getClass().getSimpleName() + ":" + method.getName();
    }
}

接口定义(源码见 IdempotentNameGenerator.java):

public interface IdempotentNameGenerator {

    /**
     * 生成幂等名称
     *
     * @param target 目标实例
     * @param method 目标方法
     * @param args   方法参数
     * @return 幂等名称
     */
    String generate(Object target, Method method, Object... args);
}

核心依赖

依赖描述
top.continew.starter:continew-starter-cache-redisson缓存模块,提供幂等键存取(RedisUtils)
cn.hutool:hutool-cryptoHutool 加密模块(默认名称生成器用于计算参数摘要)

目录