# 幂等工具设计说明 > Author : kongxiang > > Date Time : 2023-08-27 ## 1. 概述 希望spring环境中,快速支持接口幂等能力。简单引入,自动配置。通过注解就可以直接使用。 无需复杂配置 功能: - [x] 基于spring框架 - [x] 直接进入jar,即可直接使用 - [x] 通过`@Idempotent`注解一键赋能类或方法幂等性 - [ ] 支持指定token生成算法 - [x] 可支持自定义实现token缓冲存储 ## 2. 如何引入 ```xml com.kongxiang kongxiang-spring {version} ``` ## 3. 如何使用 ### 3.1 设计思路 ![](./idempotent-class.png) #### 3.1.1 使用设计 要求:开发人员通过配置一个注解`@Idempotent`即可使注解的方法调用时,实现幂等性。 幂等性方案: - [x] 防重token令牌 设计: 1. 基于环境Spring 框架。所以采用Spring Aop 拦截注解方法和类。自定义 幂等 逻辑,实现幂等要求。 2. 梳理流程: client请求【携带token和userid】-> 服务端Aop拦截【配置@Idempotent 】-> 获取注解信息 -> 认证token和userId -> 通过,执行业务逻辑,失败返回错误信息 3. 抽象出存储接口`TokenCache`和请求体传输token方式`TokenHolder` 4. @Idempontent注解在类和方法上的优先级设计:方法上注解配置信息优先于类上注解信息。注意:只是覆盖重复配置。 5. TokenCache 存储接口实现,是抽象出的一个接口。支持存储自定义配置方案。该框架:已经实现的方式:1. InMemory 2. Reids > 根据接入环境的配置。如果已经接入`spring-boot-starter-redis`和连接redis服务成功。那么将会使用`RedisTokenCache`,否则默认使用`InMemoryTokenCache`.如果是redis,可以使用application.yml配置相关属性【为实现】 6. 设计请求体提供token接口的目的,是兼容在不同spring环境下,直接调用注解@Idempotent方法时,提供给Aop防重Token处理逻辑校验中去获取token和value的能力。如:在正常spring web环境中,采用的是HttpSerlvetRequest中去获取token和value。而在非spring环境中,也需要自动提供获取token的方式。根据web中底层实现原理。采用ThreadLocal绑定线程的特点,实现aop在内部线程获取token。 #### 3.1.2 具体接入 #### 3.1.2.1 引入包 ```xml com.kongxiang kongxiang-spring {version} ``` #### 3.1.2.2 注解方法@Idempotent 支持 注解类 和注解方法 - 注解类:类下面所有的方法被调用时,执行幂等逻辑 - 注解方法: 该方法被调用时,执行幂等逻辑 ```java @Idempotent public class HelloWorld{ public void a(){ } @Idempotent public void b(){ } } ``` #### 3.1.2.3 开发代码 - 在普通方法中调用 ```java @Compoment public class Client { /**TODO 这个目前是个具体类,后续需要改造 幂等实现方式接口IdempotentCaseHanlder,该类只是接口的一个实现类而已 */ @Autowire private AntiDuplicationTokenHanlder tokenHanlder; @Autowire private HelloWorld helloWorld;// 自己开发的幂等注解服务 private Stirng token; // 用户信息最开始就提供了 private String userInfo = getUseInfo(); // 1. 客户端先获取token public void requestGetToken(String userInfo){ this.token = tokenHandler.getToken(userInfo); } // 2. 执行幂等函数 public void execute(){ requestGetToken(userInfo ); // 传递参数 // 不加这行,该线程传递的参数是之前的。userInfo验证不通过。也不会执行业务逻辑 TokenHolder.setToken(this.token,this.userInfo); // 执行幂等操作 helloWorld.b(); //第二次和之后操作会报错 helloWorld.b(); // ------- requestGetToken(userInfo+"1"); TokenHolder.setToken(this.token,this.userInfo); helloWorld.b();// 报错 userInfo 验证失败 TokenHolder.setToken(this.token,this.userInfo+"1"); helloWorld.b();// 成功 helloWorld.b();// 报错 userInfo 验证失败 } } ``` - 在spring web中接口中使用 1. 实现 getToken接口【必须提供给客户端获取token的接口】 2. 在注解使用 ```java @RestController @RequestMapping // 注解类:类中所有方法都幂等 @Idempotent public class Service{ /**TODO 这个目前是个具体类,后续需要改造 幂等实现方式接口IdempotentCaseHanlder,该类只是接口的一个实现类而已 */ @Autowire private AntiDuplicationTokenHanlder tokenHanlder; // [防重token令牌方案必须提供给客户端,其他自定义方案不定] @RequestMapping("/getToken") public String getToken(){ return tokenHanlder.getToken(); } @RequestMapping("/login") // 只有该方法幂等 @Idempotent public Object login(){ ... } } ```