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 在指定的有效期内再次发起请求时,会被拦截器识别并阻止其重复执行业务逻辑

相关推荐
昭阳~26 分钟前
MySQL读写分离
数据库·mysql
字节源流2 小时前
关于maven的依赖下不下来的问题
java·maven
pjx9873 小时前
服务间的“握手”:OpenFeign声明式调用与客户端负载均衡
java·运维·spring·负载均衡
jjkkzzzz3 小时前
Linux下的c/c++开发之操作Redis数据库
数据库·c++·redis
prinrf('千寻)4 小时前
MyBatis-Plus 的 updateById 方法不更新 null 值属性的问题
java·开发语言·mybatis
老华带你飞4 小时前
实习记录小程序|基于SSM+Vue的实习记录小程序设计与实现(源码+数据库+文档)
java·数据库·spring boot·小程序·论文·毕设·实习记录小程序
在未来等你4 小时前
互联网大厂Java求职面试:AI与大模型应用集成及云原生挑战
java·微服务·ai·kubernetes·大模型·embedding·spring ai
源码技术栈4 小时前
SaaS基于云计算、大数据的Java云HIS平台信息化系统源码
java·大数据·云计算·云his·his系统·云医院·区域his
编程、小哥哥4 小时前
互联网大厂Java面试:从Spring Boot到微服务架构的技术深挖
java·spring boot·redis·微服务·prometheus·面试技巧