Mastering Retry Logic in Java: Spring‑Retry vs Guava‑Retry
This article explains how to implement reliable retry mechanisms in Java applications using Spring‑Retry and Guava‑Retry, covering Maven dependencies, template‑based and annotation‑based configurations, policy and back‑off options, code examples, test results, and a practical comparison of both frameworks.
Spring Retry Overview
Spring Retry provides declarative retry support for Spring applications. It wraps operations that may throw exceptions and retries them according to configurable policies.
1. Basic usage with RetryTemplate
Add Maven dependency:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>Define a task method that may return a boolean or throw an exception:
package com.zgd.demo.thread.retry;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.remoting.RemoteAccessException;
@Slf4j
public class RetryDemoTask {
public static boolean retryTask(String param) {
log.info("Received param: {}", param);
int i = RandomUtils.nextInt(0, 11);
if (i == 0) {
throw new IllegalArgumentException("Parameter error");
} else if (i == 1) {
return true;
} else if (i == 2) {
return false;
} else {
throw new RemoteAccessException("Remote access error");
}
}
}Configure a RetryTemplate with a SimpleRetryPolicy (max attempts) and a FixedBackOffPolicy (interval):
package com.zgd.demo.thread.retry.spring;
import com.zgd.demo.thread.retry.RetryDemoTask;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import java.util.Collections;
@Slf4j
public class SpringRetryTemplateTest {
private static final long BACKOFF_PERIOD = 1000L; // 1 s
private static final int MAX_ATTEMPTS = 3;
@Test
public void test() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy backOff = new FixedBackOffPolicy();
backOff.setBackOffPeriod(BACKOFF_PERIOD);
retryTemplate.setBackOffPolicy(backOff);
SimpleRetryPolicy policy = new SimpleRetryPolicy(
MAX_ATTEMPTS,
Collections.singletonMap(RemoteAccessException.class, true));
retryTemplate.setRetryPolicy(policy);
Boolean result = retryTemplate.execute(
ctx -> {
boolean r = RetryDemoTask.retryTask("abc");
log.info("Call result: {}", r);
return r;
},
ctx -> {
log.info("Retries exhausted or non‑retryable exception");
return false;
});
log.info("Final result: {}", result);
}
}The RetryTemplate executes a RetryCallback (business logic) and, if retries are exhausted, a RecoveryCallback.
Retry policies provided by Spring Retry
NeverRetryPolicy – never retries.
AlwaysRetryPolicy – retries indefinitely (use with caution).
SimpleRetryPolicy – fixed number of attempts (default 3).
TimeoutRetryPolicy – retries until a timeout expires.
ExceptionClassifierRetryPolicy – different policies per exception type.
CircuitBreakerRetryPolicy – adds circuit‑breaker parameters openTimeout, resetTimeout, delegate.
CompositeRetryPolicy – combines multiple policies (optimistic or pessimistic).
Back‑off strategies
NoBackOffPolicy – immediate retry.
FixedBackOffPolicy – constant wait period (default 1 s).
UniformRandomBackOffPolicy – random wait between minBackOffPeriod (default 500 ms) and maxBackOffPeriod (default 1500 ms).
ExponentialBackOffPolicy – exponential increase; configurable initialInterval (default 100 ms), maxInterval (default 30 s), and multiplier.
ExponentialRandomBackOffPolicy – exponential back‑off with random multiplier.
Annotation‑based usage
Add AspectJ weaver in addition to Spring Retry:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>Enable retry support on a Spring Boot application:
@EnableRetry
public class Application { … }Define a service method with @Retryable and a recovery method with @Recover:
package com.zgd.demo.thread.retry.spring;
import com.zgd.demo.thread.retry.RetryDemoTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class SpringRetryDemo {
@Retryable(
value = {RemoteAccessException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 2000L, multiplier = 2))
public boolean call(String param) {
return RetryDemoTask.retryTask(param);
}
@Recover
public boolean recover(Exception e, String param) {
log.error("Retries exhausted or non‑retryable exception", e);
return false;
}
}When RemoteAccessException is thrown, the method is retried up to three times with an initial 2 s delay that doubles on each attempt (2 s, 4 s, 6 s). Exceptions not listed in value (e.g., IllegalArgumentException) bypass the retry mechanism and invoke the @Recover method directly.
Guava‑Retry Overview
Guava‑retry provides a thread‑safe retry mechanism based on java.util.concurrent.Callable. It can retry based on exceptions *and* on the callable’s return value.
Add Maven dependency:
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>Example using RetryerBuilder:
package com.zgd.demo.thread.retry.guava;
import com.github.rholder.retry.*;
import com.zgd.demo.thread.retry.RetryDemoTask;
import org.springframework.remoting.RemoteAccessException;
import java.util.concurrent.TimeUnit;
public class GuavaRetryTest {
public void run() throws Exception {
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfExceptionOfType(RemoteAccessException.class)
.retryIfResult(res -> !res) // retry when result is false
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
retryer.call(() -> RetryDemoTask.retryTask("abc"));
}
}Key configuration options:
retryIfException – retry on any exception.
retryIfRuntimeException – retry only on runtime exceptions.
retryIfExceptionOfType – retry on specific exception classes (e.g., NullPointerException).
retryIfResult – retry based on the callable’s return value (e.g., retryIfResult(Predicates.equalTo(false))).
withWaitStrategy – fixed, uniform random, exponential, etc.
withStopStrategy – stop after a number of attempts or after a timeout.
withRetryListener – register a RetryListener to receive callbacks after each failed attempt.
Comparison
Both Spring Retry and Guava‑Retry are thread‑safe and suitable for concurrent scenarios. Spring Retry integrates naturally with Spring’s annotation model and retries solely based on exception types. Guava‑Retry offers additional flexibility by allowing result‑based retry conditions and more granular strategy composition.
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.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
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.
