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.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
Mastering Retry Logic in Java: Spring‑Retry vs Guava‑Retry

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);
    }
}
RetryTemplate

combines 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendjavatestingspringRetryGuavaBackoff
IT Architects Alliance
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.