Mastering Retry Logic in Spring and Guava: A Practical Guide
This article explains how to implement robust retry mechanisms in Java applications using Spring‑Retry and Guava‑Retry, covering dependency setup, template configuration, annotation usage, various retry and back‑off policies, and practical code examples with detailed explanations.
Spring‑Retry Framework
Spring Retry provides declarative retry support for Spring applications, useful in batch processing, Spring Integration, Hadoop, and other scenarios where operations may throw exceptions and need controlled retries.
1. Basic Usage
Preparation
Add the Maven dependency:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>Create a task method that randomly throws exceptions or returns values:
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);
log.info("Random number:{}", i);
if (i == 0) {
log.info("Zero, throw IllegalArgumentException");
throw new IllegalArgumentException("Parameter error");
} else if (i == 1) {
log.info("One, return true");
return true;
} else if (i == 2) {
log.info("Two, return false");
return false;
} else {
log.info("Greater than 2, throw RemoteAccessException");
throw new RemoteAccessException("Remote access error");
}
}
}2. Using SpringRetryTemplate
Configure a RetryTemplate with a retry policy and a back‑off policy:
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(1000L); // 1 second
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(3, exceptionMap);
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);Execute the retry logic:
Boolean result = retryTemplate.execute(
retryContext -> {
boolean b = RetryDemoTask.retryTask("abc");
log.info("Result:{}", b);
return b;
},
retryContext -> {
log.info("Max attempts reached or non‑retryable exception");
return false;
}
);
log.info("Final result:{}", result);The framework provides several built‑in retry policies such as NeverRetryPolicy , AlwaysRetryPolicy , SimpleRetryPolicy , TimeoutRetryPolicy , ExceptionClassifierRetryPolicy , CircuitBreakerRetryPolicy , and CompositeRetryPolicy . Back‑off strategies include NoBackOffPolicy , FixedBackOffPolicy , UniformRandomBackOffPolicy , ExponentialBackOffPolicy , and ExponentialRandomBackOffPolicy . Images illustrating the flow are shown below:
3. Annotation‑Based Usage
Add @EnableRetry to the Spring Boot application class and use @Retryable on methods that need retry, together with @Recover for fallback logic.
@EnableRetry
public class Application { ... }
@Service
@Slf4j
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("Max attempts reached or non‑retryable exception", e);
return false;
}
}Test the service with a SpringBootTest:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Slf4j
public class SpringRetryDemoTest extends MyBaseTest {
@Autowired
private SpringRetryDemo springRetryDemo;
@Test
public void retry() {
boolean result = springRetryDemo.call("abc");
log.info("--Result:{}--", result);
}
}Guava‑Retry Framework
Guava‑Retry offers similar functionality with more flexible retry sources, allowing retries based on exceptions or method return values.
Add the Maven dependency:
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>Build a Retryer that retries on RemoteAccessException or when the result is false:
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfExceptionOfType(RemoteAccessException.class)
.retryIfResult(res -> res == false)
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
try {
retryer.call(() -> RetryDemoTask.retryTask("abc"));
} catch (Exception e) {
e.printStackTrace();
}Guava‑Retry also supports policies such as retryIfException, retryIfRuntimeException, retryIfExceptionOfType, retryIfResult, and listeners via RetryListener to log each attempt.
Conclusion
Both Spring‑Retry and Guava‑Retry are thread‑safe and suitable for concurrent scenarios. They decouple normal logic from retry logic, support timeout, retry count, interval configuration, and listeners. Guava‑Retry is more convenient for result‑based retries, while Spring‑Retry focuses on exception‑based retries.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
