Implementing Robust Business Retry with Spring AOP and Annotations
This article explains how to implement a flexible business retry mechanism in Spring using custom annotations and AOP, covering the rationale, code examples for the @Retry annotation, RetryAspect, transaction considerations, and alternative Spring Retry library integration.
Environment: Spring 5.3.23
1. Introduction
In software development, business retry is a common requirement to ensure correctness and reliability when operations fail. Spring AOP provides a powerful way to implement efficient and reliable retry handling.
2. Why Retry?
Improve system reliability : Network delays, service failures, or timeouts can cause request failures; retries increase reliability.
Ensure data consistency : In financial or e‑commerce systems, a failed operation may lead to inconsistent data; retries help maintain consistency.
Optimize user experience : Allowing retries gives users a tolerance for transient errors.
Prevent occasional mistakes : Simple input or operation errors can be corrected by retrying.
3. Retry Implementation
By combining a custom annotation with AOP, we can create a flexible retry mechanism. First, define the @Retry annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Retry {
// Number of retry attempts
int nums() default 3;
}Next, implement an aspect that intercepts methods annotated with @Retry:
@Aspect
public class RetryAspect {
@Around("@annotation(retry)")
public Object retry(ProceedingJoinPoint pjp, Retry retry) throws Throwable {
int maxRetries = retry.nums();
int numAttempts = 0;
RuntimeException rt;
do {
numAttempts++;
try {
return pjp.proceed();
} catch (RuntimeException ex) {
System.err.printf("Retry %d...%n", numAttempts);
rt = ex;
// Sleep 1 second before next retry
TimeUnit.SECONDS.sleep(1);
}
} while (numAttempts <= maxRetries);
// All retries exhausted, rethrow exception
throw rt;
}
}Testing the retry logic:
public class UserService {
@Retry
public void save(int a) {
if (a == 3) {
throw new RuntimeException("参数错误");
}
System.out.println("保存成功");
}
}Running the test produces:
发生异常,进行重试
发生异常,进行重试
发生异常,进行重试
发生异常,进行重试
Exception in thread "main" java.lang.RuntimeException: 参数错误The first call fails and triggers three retries before the exception is finally thrown.
4. Working with Transactions
If the business method runs within a transaction, each retry should execute in a new transaction to avoid stale reads under repeatable‑read isolation. Adding @Order(-1) to the RetryAspect ensures it runs before the transaction aspect:
@Aspect
@Order(-1)
public class RetryAspect {
}Alternatively, Spring provides a dedicated spring-retry library that can be added via Maven:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>${spring-retry.version}</version>
</dependency>That library offers extensive retry capabilities.
End of article.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
