Custom @FeignRetry Annotation for Per‑Method Retry in Feign Clients Using Spring Retry
This article explains how to create a custom @FeignRetry annotation that leverages Spring Retry to provide configurable, per‑method retry policies for Feign client calls, including code examples of the annotation, aspect implementation, and usage patterns.
Custom @FeignRetry Annotation
In our company, services communicate via Feign, but the built‑in Feign retry mechanism applies globally and cannot be configured per method. By combining Spring‑Retry with Feign, we can define a custom @FeignRetry annotation that allows different retry policies for each Feign client method.
Definition of @FeignRetry
The annotation mirrors @Retryable and includes attributes such as backoff, maxAttempt, and include. Its source code is shown below.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignRetry {
Backoff backoff() default @Backoff();
int maxAttempt() default 3;
Class
[] include() default {};
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Backoff {
long delay() default 1000L;;
long maxDelay() default 0L;;
double multiplier() default 0.0D;;
}The corresponding aspect FeignRetryAspect intercepts methods annotated with @FeignRetry , builds a RetryTemplate with the specified back‑off and retry policies, and executes the original call.
Slf4j
@Aspect
@Component
public class FeignRetryAspect {
@Around("@annotation(FeignRetry)")
public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getCurrentMethod(joinPoint);
FeignRetry feignRetry = method.getAnnotation(FeignRetry.class);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(prepareBackOffPolicy(feignRetry));
retryTemplate.setRetryPolicy(prepareSimpleRetryPolicy(feignRetry));
// retry
return retryTemplate.execute(arg0 -> {
int retryCount = arg0.getRetryCount();
log.info("Sending request method: {}, max attempt: {}, delay: {}, retryCount: {}",
method.getName(),
feignRetry.maxAttempt(),
feignRetry.backoff().delay(),
retryCount
);
return joinPoint.proceed(joinPoint.getArgs());
});
}
private BackOffPolicy prepareBackOffPolicy(FeignRetry feignRetry) {
if (feignRetry.backoff().multiplier() != 0) {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(feignRetry.backoff().delay());
backOffPolicy.setMaxInterval(feignRetry.backoff().maxDelay());
backOffPolicy.setMultiplier(feignRetry.backoff().multiplier());
return backOffPolicy;
} else {
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(feignRetry.backoff().delay());
return fixedBackOffPolicy;
}
}
private SimpleRetryPolicy prepareSimpleRetryPolicy(FeignRetry feignRetry) {
Map
, Boolean> policyMap = new HashMap<>();
policyMap.put(RetryableException.class, true); // Connection refused or time out
policyMap.put(ClientException.class, true); // Load balance does not available (cause of RunTimeException)
if (feignRetry.include().length != 0) {
for (Class
t : feignRetry.include()) {
policyMap.put(t, true);
}
}
return new SimpleRetryPolicy(feignRetry.maxAttempt(), policyMap, true);
}
private Method getCurrentMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return signature.getMethod();
}
}The aspect captures methods annotated with @FeignRetry , passes the configuration to Spring's RetryTemplate , and invokes the target service according to the defined retry strategy.
Usage of @FeignRetry
To enable retry for a specific Feign client method, simply place @FeignRetry on the method and specify the desired attributes. The example below demonstrates two methods with different retry configurations.
@GetMapping
@FeignRetry(maxAttempt = 3, backoff = @Backoff(delay = 500L))
ResponseEntity
retrieve1();
@GetMapping
@FeignRetry(maxAttempt = 6, backoff = @Backoff(delay = 500L, maxDelay = 20000L, multiplier = 4))
ResponseEntity
retrieve2();Additionally, add @EnableRetry to the Spring Boot application class to activate Spring Retry support.
Conclusion
Feign retry is a common scenario; by defining a custom @FeignRetry annotation, developers can apply fine‑grained, per‑method retry strategies to Feign interfaces, making the solution flexible and easy to integrate into existing projects.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.