Spring boot 注解实现幂等性

1. 添加 Spring AOP 依赖

在 pom.xml 中添加如下依赖:

XML 复制代码
<dependencies>
  <!-- Spring AOP dependency -->
  <dependency>
    <groupIdorg.springframework.boot</groupId>
      <artifactIdspring-boot-starter-aop</artifactId>
      </dependency>
</dependencies>

2. 创建自定义幂等性注解

创建一个新的 Java 注解类,通过 @interface 关键字来定义,并可以添加元注解以及属性。

java 复制代码
/**
* 《像乌鸦一样思考》让孩子学会观察和独立思考!
* https://www.sanzhiwa.top/6718.html
*/
@Retention(RetentionPolicy.RUNTIME) // 程序运行时有效
@Target(ElementType.METHOD) // 方法注解
public @interface Idempotent {
    /**
     * 请求标识符的参数名称,默认为"requestId"
     */
    String requestId() default "requestId";
    /**
     * 幂等有效时长(单位:秒)
     */
    int expireTime() default 60;
}

3. 创建拦截器

java 复制代码
/**
* 清华附小1-6年级趣味英语动画
* https://www.sanzhiwa.top/6711.html
*/
@Component
public class IdempotentInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Method method = ((HandlerMethod) handler).getMethod();
        Idempotent idempotent = method.getAnnotation(Idempotent.class);
        if (idempotent != null) {
            // 获取请求中的唯一标识符
            String requestId = obtainRequestId(request, idempotent.requestId());
            // 判断该请求是否已经处理过
            if (redisTemplate.opsForValue().get(idempotentKey(requestId)) != null) {
                // 已经处理过,返回幂等响应
                response.getWriter().write("重复请求");
                return false;
            } else {
                // 将请求标识符存入Redis,并设置过期时间
                redisTemplate.opsForValue().set(idempotentKey(requestId), "processed", idempotent.expireTime(), TimeUnit.SECONDS);
                return true; // 继续执行业务逻辑
            }
        }
        return super.preHandle(request, response, handler);
    }

    private String idempotentKey(String requestId) {
        return "idempotent:" + requestId;
    }

    private String obtainRequestId(HttpServletRequest request, String paramName) {
        // 实现从请求中获取唯一标识符的方法
        return request.getParameter(paramName);
    }
}

4. 配置拦截器

在 Spring Boot 配置文件类中,添加拦截器配置:

java 复制代码
/**
* 儿童新概念英语
* https://www.sanzhiwa.top/6696.html
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private IdempotentInterceptor idempotentInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(idempotentInterceptor)
        	.addPathPatterns("/**"); // 拦截所有接口
    }
}

5.使用自定义注解

最后,在需要进行幂等控制的 Controller 方法上使用 @Idempotent 注解:

java 复制代码
/**
*
* 微观小世界】奇趣探险,启程进入神秘的微观世界!
* https://www.sanzhiwa.top/6704.html
*/
@RestController
public class TestController {
    @PostMapping("/order")
    @Idempotent(requestId = "orderId") // 假设orderId是从客户端传来的唯一标识订单请求的参数
    public String placeOrder(@RequestParam("orderId") String orderId, ...) {
        // 业务处理逻辑
    }
}

这样,当有相同的请求 ID 在指定的有效期内再次发起请求时,会被拦截器识别并阻止其重复执行业务逻辑

相关推荐
TDengine (老段)14 分钟前
TDengine 快速体验(Docker 镜像方式)
大数据·数据库·物联网·docker·时序数据库·tdengine·涛思数据
啾啾Fun27 分钟前
Java反射操作百倍性能优化
java·性能优化·反射·缓存思想
笨笨马甲29 分钟前
附加模块--Qt SQL模块功能及架构解析
数据库·sql·qt
20岁30年经验的码农34 分钟前
若依微服务Openfeign接口调用超时问题
java·微服务·架构
独爱竹子的功夫熊猫34 分钟前
数据库技巧:INSERT IGNORE的高效插入策略
数据库·后端·mysql
曲莫终42 分钟前
SpEl表达式之强大的集合选择(Collection Selection)和集合投影(Collection Projection)
java·spring boot·spring
时序数据说1 小时前
时序数据库IoTDB数据模型建模实例详解
大数据·数据库·开源·时序数据库·iotdb
水水沝淼㵘1 小时前
嵌入式开发学习日志(数据库II && 网页制作)Day38
服务器·c语言·网络·数据结构·数据库·学习
ajassi20001 小时前
开源 java android app 开发(十二)封库.aar
android·java·linux·开源
时序数据说1 小时前
时序数据库IoTDB结合SeaTunnel实现高效数据同步
大数据·数据库·开源·时序数据库·iotdb