I want to implement a retry mechanism at the application level in a Spring Boot application. However, I don't want to add @Retryable or make any changes to the service methods or their classes. My goal is to implement the retry logic using Spring AOP and a centralized configuration, such as RetryTemplate, for consistency across the application.
Here’s what I have done so far:
Aspect Class
I created an aspect that uses RetryTemplate to handle retries for all methods within @Service classes.
@Aspect
@Component
public class DbConnectionRetryAspect {
    private final RetryTemplate retryTemplate;
    public DbConnectionRetryAspect(RetryTemplate retryTemplate) {
        this.retryTemplate = retryTemplate;
    }
    @Around("@within(org.springframework.stereotype.Service)")
    public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Aspect invoked for method: " + joinPoint.getSignature());
        return retryTemplate.execute(context -> {
            System.out.println("Retry attempt: " + context.getRetryCount());
            try {
                return joinPoint.proceed();
            } catch (SQLTransientConnectionException e) {
                System.out.println("Retryable exception caught: " + e.getMessage());
                throw e; // Ensure the exception propagates for retries
            }
        });
    }
}
RetryTemplate Configuration
I created a RetryTemplate bean to define the retry logic.
@Bean
public RetryTemplate retryTemplate() {
    RetryTemplate retryTemplate = new RetryTemplate();
    SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
        5,  // maxAttempts
        Map.of(SQLTransientConnectionException.class, true)
    );
    retryTemplate.setRetryPolicy(retryPolicy);
    FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
    backOffPolicy.setBackOffPeriod(1000); // 1 second
    retryTemplate.setBackOffPolicy(backOffPolicy);
    return retryTemplate;
}
Service Class
The service method throws a SQLTransientConnectionException when the operation fails. I don’t want to annotate this method with @Retryable.
@Service
public class MyService {
    public void performOperation() throws SQLTransientConnectionException {
        System.out.println("Service method invoked");
        throw new SQLTransientConnectionException("Simulated transient error");
    }
}
Configuration I added the following configuration to enable AspectJ:
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AspectJConfig {
}
The Problem: While the aspect is being invoked, the retry logic is not working as expected. Specifically:
The RetryTemplate logs indicate that retries are attempted (Retry attempt: X), but the service method is only called once.
It seems the exception is not propagating correctly or the retry logic is not being applied to the service method.
What I Need Help With:
How can I implement application-level retry logic using Spring AOP and RetryTemplate such that:
@Retryable).Any guidance or suggestions would be greatly appreciated.
Thank you!
I want to implement a retry mechanism at the application level in a Spring Boot application. However, I don't want to add @Retryable or make any changes to the service methods or their classes. My goal is to implement the retry logic using Spring AOP and a centralized configuration, such as RetryTemplate, for consistency across the application.
Here’s what I have done so far:
Aspect Class
I created an aspect that uses RetryTemplate to handle retries for all methods within @Service classes.
@Aspect
@Component
public class DbConnectionRetryAspect {
    private final RetryTemplate retryTemplate;
    public DbConnectionRetryAspect(RetryTemplate retryTemplate) {
        this.retryTemplate = retryTemplate;
    }
    @Around("@within(org.springframework.stereotype.Service)")
    public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Aspect invoked for method: " + joinPoint.getSignature());
        return retryTemplate.execute(context -> {
            System.out.println("Retry attempt: " + context.getRetryCount());
            try {
                return joinPoint.proceed();
            } catch (SQLTransientConnectionException e) {
                System.out.println("Retryable exception caught: " + e.getMessage());
                throw e; // Ensure the exception propagates for retries
            }
        });
    }
}
RetryTemplate Configuration
I created a RetryTemplate bean to define the retry logic.
@Bean
public RetryTemplate retryTemplate() {
    RetryTemplate retryTemplate = new RetryTemplate();
    SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
        5,  // maxAttempts
        Map.of(SQLTransientConnectionException.class, true)
    );
    retryTemplate.setRetryPolicy(retryPolicy);
    FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
    backOffPolicy.setBackOffPeriod(1000); // 1 second
    retryTemplate.setBackOffPolicy(backOffPolicy);
    return retryTemplate;
}
Service Class
The service method throws a SQLTransientConnectionException when the operation fails. I don’t want to annotate this method with @Retryable.
@Service
public class MyService {
    public void performOperation() throws SQLTransientConnectionException {
        System.out.println("Service method invoked");
        throw new SQLTransientConnectionException("Simulated transient error");
    }
}
Configuration I added the following configuration to enable AspectJ:
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AspectJConfig {
}
The Problem: While the aspect is being invoked, the retry logic is not working as expected. Specifically:
The RetryTemplate logs indicate that retries are attempted (Retry attempt: X), but the service method is only called once.
It seems the exception is not propagating correctly or the retry logic is not being applied to the service method.
What I Need Help With:
How can I implement application-level retry logic using Spring AOP and RetryTemplate such that:
@Retryable).Any guidance or suggestions would be greatly appreciated.
Thank you!
The issue was caused by the @Transactional annotation on my custom @CustomService annotation. Since @Transactional proxies the class to handle transaction management, it interfered with the retry mechanism when combined with @Service. The retry logic was being applied to the transactional proxy, which was likely causing the method invocation to bypass the retry logic after the first call.

AspectJConfigas Spring Boot takes care of that. The method not being invoked again the only thing I could think of is caching of the method result due to the use of@Cacheableor some other means of caching. – M. Deinum Commented Jan 16 at 13:13