Backend Development 8 min read

Implementing Idempotency in Spring Boot Using Redis and Custom Annotations

This article explains the concept of idempotency, identifies which HTTP operations are naturally idempotent, describes why idempotency is needed in scenarios like timeout retries, async callbacks and message queues, and provides a complete Spring Boot implementation using a custom annotation, Redis storage, AOP interception, token generation and example controllers.

Architecture Digest
Architecture Digest
Architecture Digest
Implementing Idempotency in Spring Boot Using Redis and Custom Annotations

Idempotency ensures that multiple identical requests produce the same result as a single request, which is crucial for reliable backend services.

Requests that are naturally idempotent include query operations and most delete operations, while updates and inserts can be non‑idempotent unless special handling such as unique constraints is used.

The article discusses three main scenarios that require idempotency: timeout retries, asynchronous callbacks, and message‑queue consumption, each of which may deliver duplicate requests.

To achieve idempotency, the article proposes using a unique token (idempotent token) generated per request, stored in a persistent store such as Redis, and validated before processing.

A custom Java annotation @Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { String name() default ""; String field() default ""; Class type(); } is defined with attributes for the parameter name, field, and type. A data‑transfer object public class RequestData { private Header header; private T body; } public class Header { private String token; } containing a Header with the token is used as the method argument.

An AOP aspect @Aspect @Component public class IdempotentAspect { @Resource private RedisIdempotentStorage redisIdempotentStorage; @Pointcut("@annotation(com.springboot.micrometer.annotation.Idempotent)") public void idempotent() {} @Around("idempotent()") public Object methodAround(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Idempotent idempotent = method.getAnnotation(Idempotent.class); String field = idempotent.field(); String name = idempotent.name(); Class clazzType = idempotent.type(); String token = ""; Object object = clazzType.newInstance(); Map paramValue = AopUtils.getParamValue(joinPoint); if (object instanceof RequestData) { RequestData idempotentEntity = (RequestData) paramValue.get(name); token = String.valueOf(AopUtils.getFieldValue(idempotentEntity.getHeader(), field)); } if (redisIdempotentStorage.delete(token)) { return joinPoint.proceed(); } return "重复请求"; } } intercepts methods annotated with @Idempotent , extracts the token, checks Redis via RedisIdempotentStorage , and either proceeds with the method or returns a duplicate‑request response.

A token‑generation controller provides an endpoint to obtain a token, which is saved in Redis with a short TTL: @RestController @RequestMapping("/idGenerator") public class IdGeneratorController { @Resource private RedisIdempotentStorage redisIdempotentStorage; @RequestMapping("/getIdGeneratorToken") public String getIdGeneratorToken() { String generateId = IdGeneratorUtil.generateId(); redisIdempotentStorage.save(generateId); return generateId; } } The business controller then uses the token in the request header and applies the @Idempotent annotation to enforce single execution: @RestController @RequestMapping("/order") public class OrderController { @RequestMapping("/saveOrder") @Idempotent(name = "requestData", type = RequestData.class, field = "token") public String saveOrder(@RequestBody RequestData requestData) { return "success"; } }

Example request flow: obtain token → call /order/saveOrder with token → first call succeeds, second call with the same token is rejected, demonstrating idempotent behavior.

backendJavaAOPRedisSpring BootIdempotencyAnnotation
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.