@Configuration
@EnableResilientMethods
public class ResilienceConfig {}@Service
public class ApiService {
// Basic retry
@Retryable(
value = IOException.class,
maxRetries = 3,
delay = 1000
)
public String callApi(String url) throws IOException {
return httpClient.get(url);
}
// Exponential backoff
@Retryable(
includes = {SocketTimeoutException.class, ConnectException.class},
maxRetries = 5,
delay = 1000, // 1s initial
multiplier = 2.0, // Double each time
maxDelay = 10000, // Cap at 10s
jitter = 500 // Random ±500ms
)
public Response retryWithBackoff(Request req) {
return apiClient.execute(req);
}
// Property-based configuration
@Retryable(
maxRetriesString = "${api.retry.max:3}",
delayString = "${api.retry.delay:1s}",
multiplierString = "${api.retry.multiplier:1.5}"
)
public Data configurable() {
return service.fetch();
}
// Exclude exceptions
@Retryable(
includes = Exception.class,
excludes = {IllegalArgumentException.class, NullPointerException.class},
maxRetries = 3
)
public void selectiveRetry() {
// Retries any exception except IllegalArgumentException and NPE
}
// Custom predicate
@Retryable(
predicate = TransientErrorPredicate.class,
maxRetries = 3,
delay = 1000
)
public Result customPredicate() {
return compute();
}
}
public class TransientErrorPredicate implements MethodRetryPredicate {
@Override
public boolean shouldRetry(Method method, Throwable throwable) {
return throwable instanceof TransientException
&& ((TransientException) throwable).isRetryable();
}
}@Service
@ConcurrencyLimit(10) // Shared limit for all methods
public class DatabaseService {
public User findUser(Long id) {
return userRepository.findById(id);
}
// Method-specific limit
@ConcurrencyLimit(3)
public void expensiveOperation(String data) {
// Only 3 concurrent executions
}
// Property-based limit
@ConcurrencyLimit(limitString = "${db.concurrency:5}")
public List<Order> findOrders(Long userId) {
return orderRepository.findByUserId(userId);
}
}
// Serial execution (mutex)
@Service
public class SingleThreadService {
@ConcurrencyLimit(1)
public void synchronizedOperation() {
// Only one concurrent execution at a time
}
}@Service
public class ReactiveApiService {
// Works with Mono/Flux
@Retryable(
value = WebClientException.class,
maxRetries = 5,
delay = 1000,
multiplier = 2.0
)
public Mono<String> fetchDataReactive(String url) {
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class);
}
@Retryable(maxRetries = 3)
public Flux<Item> streamItems() {
return webClient.get()
.uri("/items")
.retrieve()
.bodyToFlux(Item.class);
}
}@Configuration
public class RetryConfig {
@Bean
public RetryInterceptor retryInterceptor() {
MethodRetrySpec spec = new MethodRetrySpec(
(method, throwable) -> throwable instanceof IOException,
3, // maxRetries
Duration.ofSeconds(1), // delay
Duration.ofMillis(500), // jitter
2.0, // multiplier
Duration.ofSeconds(10) // maxDelay
);
return new SimpleRetryInterceptor(spec);
}
}// Only enable retry
@Configuration
public class RetryOnlyConfig {
@Bean
public RetryAnnotationBeanPostProcessor retryProcessor() {
return new RetryAnnotationBeanPostProcessor();
}
}
// Only enable concurrency limiting
@Configuration
public class ConcurrencyOnlyConfig {
@Bean
public ConcurrencyLimitBeanPostProcessor concurrencyProcessor() {
return new ConcurrencyLimitBeanPostProcessor();
}
}