Mastering Retry Logic in Java: Spring‑Retry vs Guava‑Retry
This guide explains how to implement declarative retry mechanisms in Java using Spring‑Retry and Guava‑Retry, covering dependency setup, template and annotation usage, various retry and back‑off policies, test examples, and a comparative analysis of their flexibility and configuration options.
Spring Retry Overview
Spring Retry provides declarative retry support for Spring applications. It can be used in Spring Batch, Spring Integration, Hadoop, etc., to automatically retry operations that throw exceptions according to a configurable policy.
1. Maven Dependency
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>2. Example Task Method
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 {
/** Randomly returns true, false or throws an exception. */
public static boolean retryTask(String param) {
log.info("Received param: {}", param);
int i = RandomUtils.nextInt(0, 11);
log.info("Generated number: {}", i);
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");
}
}
}3. Programmatic Use with RetryTemplate
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 SpringRetryTemplateDemo {
private static final long FIXED_BACKOFF_MS = 1000L; // 1 s between attempts
private static final int MAX_ATTEMPTS = 3; // default max attempts
@Test
public void executeWithRetry() {
// Define which exception types should trigger a retry
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
MAX_ATTEMPTS,
Collections.singletonMap(RemoteAccessException.class, true));
// Fixed interval back‑off
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(FIXED_BACKOFF_MS);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(backOffPolicy);
Boolean result = retryTemplate.execute(
retryContext -> {
boolean value = RetryDemoTask.retryTask("abc");
log.info("Task returned: {}", value);
return value;
},
retryContext -> {
log.info("Max attempts reached or non‑retryable exception");
return false;
});
log.info("Final result: {}", result);
}
} RetryTemplatecombines a RetryPolicy (which decides whether to retry) and a BackOffPolicy (which decides the wait time between attempts).
4. Built‑in Retry Policies (Spring Retry)
NeverRetryPolicy – one attempt only.
AlwaysRetryPolicy – retries indefinitely (use with caution).
SimpleRetryPolicy – fixed number of attempts (default 3).
TimeoutRetryPolicy – retries while total elapsed time is below a configured timeout (default 1 s).
ExceptionClassifierRetryPolicy – different retry rules per exception type.
CircuitBreakerRetryPolicy – adds circuit‑breaker semantics (requires openTimeout, resetTimeout, delegate).
CompositeRetryPolicy – combines several policies (optimistic or pessimistic).
5. Back‑off Strategies (Spring Retry)
NoBackOffPolicy – immediate retry.
FixedBackOffPolicy – constant sleep period (default 1 s, implemented with Thread.sleep).
UniformRandomBackOffPolicy – random sleep 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 jitter.
Annotation‑Based Spring Retry
1. Maven Dependencies
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>2. Enable Retry in a Spring Boot Application
@EnableRetry
@SpringBootApplication
public class Application { … }3. Service Method with @Retryable and Recovery 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 SpringRetryDemoService {
@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("Recovery after max attempts or non‑retryable exception", e);
return false;
}
}4. Test Example
package com.zgd.demo.thread.retry.spring;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@Slf4j
@SpringBootTest
public class SpringRetryDemoServiceTest {
@Autowired
private SpringRetryDemoService service;
@Test
public void testRetry() {
boolean result = service.call("abc");
log.info("Result: {}", result);
}
}Only RemoteAccessException triggers a retry. The back‑off starts at 2 s and doubles on each subsequent attempt (4 s, then 6 s). An IllegalArgumentException is not listed in value, so the @Recover method is invoked immediately.
Guava‑Retry
1. Maven Dependency
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>2. Builder‑Based Retryer
package com.zgd.demo.thread.retry.guava;
import com.github.rholder.retry.*;
import com.zgd.demo.thread.retry.RetryDemoTask;
import org.junit.Test;
import org.springframework.remoting.RemoteAccessException;
import java.util.concurrent.TimeUnit;
public class GuavaRetryDemo {
@Test
public void retryWithGuava() throws Exception {
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
// Retry on RemoteAccessException
.retryIfExceptionOfType(RemoteAccessException.class)
// Retry when the task returns false
.retryIfResult(res -> !res)
// Fixed 3‑second wait between attempts
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
// Stop after three attempts
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
// The Callable is executed; Guava will apply the configured policies
retryer.call(() -> RetryDemoTask.retryTask("abc"));
}
}3. Additional Guava Policies
retryIfException – retry on any exception.
retryIfRuntimeException – retry only on unchecked exceptions.
retryIfExceptionOfType – retry on specific exception classes (e.g., NullPointerException).
retryIfResult – retry based on the method’s return value (e.g., false, null, or a pattern).
withRetryListener – register a RetryListener to execute custom logic after each failed attempt.
.withRetryListener(new RetryListener() {
@Override
public void onRetry(Attempt attempt) {
logger.error("Attempt {} failed", attempt.getAttemptNumber());
}
})Comparison
Both Spring Retry and Guava‑Retry are thread‑safe and suitable for concurrent scenarios. They separate normal business logic from retry concerns and support configurable attempt limits, timeouts, back‑off intervals, and listeners. Guava‑Retry adds result‑based retry predicates, making it more flexible when success is defined by a return value rather than the absence of an exception.
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.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
