diff --git a/framework-docs/modules/ROOT/pages/core/resilience.adoc b/framework-docs/modules/ROOT/pages/core/resilience.adoc index f644ce40912..c937c6983e2 100644 --- a/framework-docs/modules/ROOT/pages/core/resilience.adoc +++ b/framework-docs/modules/ROOT/pages/core/resilience.adoc @@ -40,6 +40,17 @@ public void sendNotification() { NOTE: `@Retryable(MessageDeliveryException.class)` is a shortcut for `@Retryable(includes{nbsp}={nbsp}MessageDeliveryException.class)`. +[TIP] +==== +For advanced use cases, you can specify a custom `MethodRetryPredicate` via the +`predicate` attribute in `@Retryable`, and the predicate will be used to determine whether +to retry a failed method invocation based on a `Method` and a given `Throwable` – for +example, by checking the message of the `Throwable`. + +Custom predicates can be combined with `includes` and `excludes`; however, custom +predicates will always be applied after `includes` and `excludes` have been applied. +==== + Or for 5 retry attempts and an exponential back-off strategy with a bit of jitter: [source,java,indent=0,subs="verbatim,quotes"] diff --git a/spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java index f43e0a55226..fd11d055c95 100644 --- a/spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/resilience/annotation/ConcurrencyLimitBeanPostProcessor.java @@ -82,7 +82,7 @@ public class ConcurrencyLimitBeanPostProcessor extends AbstractBeanFactoryAwareA Object target = invocation.getThis(); Class targetClass = (target != null ? target.getClass() : method.getDeclaringClass()); if (target == null && invocation instanceof ProxyMethodInvocation methodInvocation) { - // Allow validation for AOP proxy without a target + // Support concurrency throttling for AOP proxy without a target target = methodInvocation.getProxy(); } Assert.state(target != null, "Target must not be null"); diff --git a/spring-context/src/main/java/org/springframework/resilience/annotation/Retryable.java b/spring-context/src/main/java/org/springframework/resilience/annotation/Retryable.java index 3f27d2083d2..b44dcdf6124 100644 --- a/spring-context/src/main/java/org/springframework/resilience/annotation/Retryable.java +++ b/spring-context/src/main/java/org/springframework/resilience/annotation/Retryable.java @@ -64,6 +64,8 @@ public @interface Retryable { /** * Applicable exception types to attempt a retry for. This attribute * allows for the convenient specification of assignable exception types. + *

This can optionally be combined with {@link #excludes() excludes} or + * a custom {@link #predicate() predicate}. *

The default is empty, leading to a retry attempt for any exception. * @see #excludes() * @see #predicate() @@ -74,6 +76,8 @@ public @interface Retryable { /** * Non-applicable exception types to avoid a retry for. This attribute * allows for the convenient specification of assignable exception types. + *

This can optionally be combined with {@link #includes() includes} or + * a custom {@link #predicate() predicate}. *

The default is empty, leading to a retry attempt for any exception. * @see #includes() * @see #predicate() @@ -81,12 +85,14 @@ public @interface Retryable { Class[] excludes() default {}; /** - * A predicate for filtering applicable exceptions for which - * an invocation can be retried. - *

The default is a retry attempt for any exception. + * A predicate for filtering applicable exceptions for which an invocation can + * be retried. *

A specified {@link MethodRetryPredicate} implementation will be instantiated * per method. It can use dependency injection at the constructor level or through * autowiring annotations, in case it needs access to other beans or facilities. + *

This can optionally be combined with {@link #includes() includes} or + * {@link #excludes() excludes}. + *

The default is a retry attempt for any exception. * @see #includes() * @see #excludes() */ diff --git a/spring-core/src/main/java/org/springframework/core/retry/RetryPolicy.java b/spring-core/src/main/java/org/springframework/core/retry/RetryPolicy.java index 3e6bd5cf27f..ea466e39ac7 100644 --- a/spring-core/src/main/java/org/springframework/core/retry/RetryPolicy.java +++ b/spring-core/src/main/java/org/springframework/core/retry/RetryPolicy.java @@ -80,8 +80,8 @@ public interface RetryPolicy { /** * Create a {@link RetryPolicy} configured with a maximum number of retry attempts. - *

The returned policy uses a fixed backoff of {@value Builder#DEFAULT_DELAY} - * milliseconds. + *

The returned policy applies to all exception types and uses a fixed backoff + * of {@value Builder#DEFAULT_DELAY} milliseconds. * @param maxAttempts the maximum number of retry attempts; * must be positive (or zero for no retry) * @see Builder#maxAttempts(long)