diff --git a/resilience4j-annotations/src/main/java/io/github/resilience4j/bulkhead/annotation/Bulkhead.java b/resilience4j-annotations/src/main/java/io/github/resilience4j/bulkhead/annotation/Bulkhead.java index cb64af4686..c034ded117 100644 --- a/resilience4j-annotations/src/main/java/io/github/resilience4j/bulkhead/annotation/Bulkhead.java +++ b/resilience4j-annotations/src/main/java/io/github/resilience4j/bulkhead/annotation/Bulkhead.java @@ -2,6 +2,11 @@ import java.lang.annotation.*; +/** + * This annotation can be applied to a class or a specific method. Applying it on a class is + * equivalent to applying it on all its public methods. If using Spring, + * {@code fallbackMethod} can be resolved using Spring Expression Language (SpEL). + */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.TYPE}) @Documented diff --git a/resilience4j-annotations/src/main/java/io/github/resilience4j/circuitbreaker/annotation/CircuitBreaker.java b/resilience4j-annotations/src/main/java/io/github/resilience4j/circuitbreaker/annotation/CircuitBreaker.java index 5ccdf882de..93afe0b0a2 100644 --- a/resilience4j-annotations/src/main/java/io/github/resilience4j/circuitbreaker/annotation/CircuitBreaker.java +++ b/resilience4j-annotations/src/main/java/io/github/resilience4j/circuitbreaker/annotation/CircuitBreaker.java @@ -21,7 +21,8 @@ * This annotation can be applied to a class or a specific method. Applying it on a class is * equivalent to applying it on all its public methods. The annotation enables backend monitoring * for all methods where it is applied. Backend monitoring is performed via a circuit breaker. See - * {@link io.github.resilience4j.circuitbreaker.CircuitBreaker} for details. + * {@link io.github.resilience4j.circuitbreaker.CircuitBreaker} for details. If using Spring, + * {@code fallbackMethod} can be resolved using Spring Expression Language (SpEL). */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.TYPE}) diff --git a/resilience4j-annotations/src/main/java/io/github/resilience4j/ratelimiter/annotation/RateLimiter.java b/resilience4j-annotations/src/main/java/io/github/resilience4j/ratelimiter/annotation/RateLimiter.java index 25cd6d2fa9..ee144950ea 100644 --- a/resilience4j-annotations/src/main/java/io/github/resilience4j/ratelimiter/annotation/RateLimiter.java +++ b/resilience4j-annotations/src/main/java/io/github/resilience4j/ratelimiter/annotation/RateLimiter.java @@ -21,7 +21,8 @@ * This annotation can be applied to a class or a specific method. Applying it on a class is * equivalent to applying it on all its public methods. The annotation enables throttling for all * methods where it is applied. Throttling monitoring is performed via a rate limiter. See {@link - * io.github.resilience4j.ratelimiter.RateLimiter} for details. + * io.github.resilience4j.ratelimiter.RateLimiter} for details. If using Spring, + * {@code fallbackMethod} can be resolved using Spring Expression Language (SpEL). */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.TYPE}) diff --git a/resilience4j-annotations/src/main/java/io/github/resilience4j/retry/annotation/Retry.java b/resilience4j-annotations/src/main/java/io/github/resilience4j/retry/annotation/Retry.java index 9f62c0afc1..3fbfcfcc24 100644 --- a/resilience4j-annotations/src/main/java/io/github/resilience4j/retry/annotation/Retry.java +++ b/resilience4j-annotations/src/main/java/io/github/resilience4j/retry/annotation/Retry.java @@ -20,7 +20,8 @@ /** * This annotation can be applied to a class or a specific method. Applying it on a class is * equivalent to applying it on all its public methods. The annotation enables backend retry for all - * methods where it is applied. Backend retry is performed via a retry + * methods where it is applied. Backend retry is performed via a retry. If using Spring, + * {@code fallbackMethod} can be resolved using Spring Expression Language (SpEL). */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.TYPE}) diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/bulkhead/configure/BulkheadAspect.java b/resilience4j-spring/src/main/java/io/github/resilience4j/bulkhead/configure/BulkheadAspect.java index 4b93f3b8e7..000d32dbeb 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/bulkhead/configure/BulkheadAspect.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/bulkhead/configure/BulkheadAspect.java @@ -23,6 +23,7 @@ import io.github.resilience4j.fallback.FallbackDecorators; import io.github.resilience4j.fallback.FallbackMethod; import io.github.resilience4j.utils.AnnotationExtractor; +import io.github.resilience4j.utils.ValueResolver; import io.vavr.CheckedFunction0; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -32,8 +33,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -64,7 +67,7 @@ * with a matching exception type as the last parameter on the annotated method */ @Aspect -public class BulkheadAspect implements Ordered { +public class BulkheadAspect implements EmbeddedValueResolverAware, Ordered { private static final Logger logger = LoggerFactory.getLogger(BulkheadAspect.class); @@ -74,6 +77,7 @@ public class BulkheadAspect implements Ordered { private final @Nullable List bulkheadAspectExts; private final FallbackDecorators fallbackDecorators; + private StringValueResolver embeddedValueResolver; public BulkheadAspect(BulkheadConfigurationProperties backendMonitorPropertiesRegistry, ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry, BulkheadRegistry bulkheadRegistry, @@ -103,21 +107,22 @@ public Object bulkheadAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, } Class returnType = method.getReturnType(); String backend = bulkheadAnnotation.name(); + String fallbackMethodValue = ValueResolver.resolve(this.embeddedValueResolver, bulkheadAnnotation.fallbackMethod()); if (bulkheadAnnotation.type() == Bulkhead.Type.THREADPOOL) { - if (StringUtils.isEmpty(bulkheadAnnotation.fallbackMethod())) { + if (StringUtils.isEmpty(fallbackMethodValue)) { return proceedInThreadPoolBulkhead(proceedingJoinPoint, methodName, returnType, backend); } - return executeFallBack(proceedingJoinPoint, bulkheadAnnotation.fallbackMethod(), method, + return executeFallBack(proceedingJoinPoint, fallbackMethodValue, method, () -> proceedInThreadPoolBulkhead(proceedingJoinPoint, methodName, returnType, backend)); } else { io.github.resilience4j.bulkhead.Bulkhead bulkhead = getOrCreateBulkhead(methodName, backend); - if (StringUtils.isEmpty(bulkheadAnnotation.fallbackMethod())) { + if (StringUtils.isEmpty(fallbackMethodValue)) { return proceed(proceedingJoinPoint, methodName, bulkhead, returnType); } - return executeFallBack(proceedingJoinPoint, bulkheadAnnotation.fallbackMethod(), method, + return executeFallBack(proceedingJoinPoint, fallbackMethodValue, method, () -> proceed(proceedingJoinPoint, methodName, bulkhead, returnType)); } @@ -259,4 +264,9 @@ private Object proceedInThreadPoolBulkhead(ProceedingJoinPoint proceedingJoinPoi public int getOrder() { return bulkheadConfigurationProperties.getBulkheadAspectOrder(); } + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } } diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerAspect.java b/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerAspect.java index eefd3da5bf..303190eb2d 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerAspect.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerAspect.java @@ -21,6 +21,7 @@ import io.github.resilience4j.fallback.FallbackDecorators; import io.github.resilience4j.fallback.FallbackMethod; import io.github.resilience4j.utils.AnnotationExtractor; +import io.github.resilience4j.utils.ValueResolver; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -29,8 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -63,7 +66,7 @@ * with a matching exception type as the last parameter on the annotated method */ @Aspect -public class CircuitBreakerAspect implements Ordered { +public class CircuitBreakerAspect implements EmbeddedValueResolverAware, Ordered { private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerAspect.class); @@ -72,6 +75,7 @@ public class CircuitBreakerAspect implements Ordered { private final @Nullable List circuitBreakerAspectExtList; private final FallbackDecorators fallbackDecorators; + private StringValueResolver embeddedValueResolver; public CircuitBreakerAspect(CircuitBreakerConfigurationProperties circuitBreakerProperties, CircuitBreakerRegistry circuitBreakerRegistry, @@ -103,11 +107,12 @@ public Object circuitBreakerAroundAdvice(ProceedingJoinPoint proceedingJoinPoint methodName, backend); Class returnType = method.getReturnType(); - if (StringUtils.isEmpty(circuitBreakerAnnotation.fallbackMethod())) { + String fallbackMethodValue = ValueResolver.resolve(this.embeddedValueResolver, circuitBreakerAnnotation.fallbackMethod()); + if (StringUtils.isEmpty(fallbackMethodValue)) { return proceed(proceedingJoinPoint, methodName, circuitBreaker, returnType); } FallbackMethod fallbackMethod = FallbackMethod - .create(circuitBreakerAnnotation.fallbackMethod(), method, + .create(fallbackMethodValue, method, proceedingJoinPoint.getArgs(), proceedingJoinPoint.getTarget()); return fallbackDecorators.decorate(fallbackMethod, () -> proceed(proceedingJoinPoint, methodName, circuitBreaker, returnType)).apply(); @@ -187,4 +192,9 @@ private Object defaultHandling(ProceedingJoinPoint proceedingJoinPoint, public int getOrder() { return circuitBreakerProperties.getCircuitBreakerAspectOrder(); } -} \ No newline at end of file + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } +} diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/ratelimiter/configure/RateLimiterAspect.java b/resilience4j-spring/src/main/java/io/github/resilience4j/ratelimiter/configure/RateLimiterAspect.java index daf016213f..7d8b60272c 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/ratelimiter/configure/RateLimiterAspect.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/ratelimiter/configure/RateLimiterAspect.java @@ -22,6 +22,7 @@ import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import io.github.resilience4j.utils.AnnotationExtractor; +import io.github.resilience4j.utils.ValueResolver; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -30,8 +31,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -64,7 +67,7 @@ */ @Aspect -public class RateLimiterAspect implements Ordered { +public class RateLimiterAspect implements EmbeddedValueResolverAware, Ordered { private static final String RATE_LIMITER_RECEIVED = "Created or retrieved rate limiter '{}' with period: '{}'; limit for period: '{}'; timeout: '{}'; method: '{}'"; private static final Logger logger = LoggerFactory.getLogger(RateLimiterAspect.class); @@ -73,6 +76,7 @@ public class RateLimiterAspect implements Ordered { private final @Nullable List rateLimiterAspectExtList; private final FallbackDecorators fallbackDecorators; + private StringValueResolver embeddedValueResolver; public RateLimiterAspect(RateLimiterRegistry rateLimiterRegistry, RateLimiterConfigurationProperties properties, @@ -110,11 +114,12 @@ public Object rateLimiterAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, methodName, name); Class returnType = method.getReturnType(); - if (StringUtils.isEmpty(rateLimiterAnnotation.fallbackMethod())) { + String fallbackMethodValue = ValueResolver.resolve(this.embeddedValueResolver, rateLimiterAnnotation.fallbackMethod()); + if (StringUtils.isEmpty(fallbackMethodValue)) { return proceed(proceedingJoinPoint, methodName, returnType, rateLimiter); } FallbackMethod fallbackMethod = FallbackMethod - .create(rateLimiterAnnotation.fallbackMethod(), method, proceedingJoinPoint.getArgs(), + .create(fallbackMethodValue, method, proceedingJoinPoint.getArgs(), proceedingJoinPoint.getTarget()); return fallbackDecorators.decorate(fallbackMethod, () -> proceed(proceedingJoinPoint, methodName, returnType, rateLimiter)).apply(); @@ -197,4 +202,9 @@ private Object handleJoinPointCompletableFuture(ProceedingJoinPoint proceedingJo public int getOrder() { return properties.getRateLimiterAspectOrder(); } + + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } } diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/retry/configure/RetryAspect.java b/resilience4j-spring/src/main/java/io/github/resilience4j/retry/configure/RetryAspect.java index 953d0beb2c..51193734a5 100644 --- a/resilience4j-spring/src/main/java/io/github/resilience4j/retry/configure/RetryAspect.java +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/retry/configure/RetryAspect.java @@ -21,6 +21,7 @@ import io.github.resilience4j.retry.RetryRegistry; import io.github.resilience4j.retry.annotation.Retry; import io.github.resilience4j.utils.AnnotationExtractor; +import io.github.resilience4j.utils.ValueResolver; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -29,8 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -60,7 +63,7 @@ * with a matching exception type as the last parameter on the annotated method */ @Aspect -public class RetryAspect implements Ordered { +public class RetryAspect implements EmbeddedValueResolverAware, Ordered { private static final Logger logger = LoggerFactory.getLogger(RetryAspect.class); private final static ScheduledExecutorService retryExecutorService = Executors @@ -70,6 +73,7 @@ public class RetryAspect implements Ordered { private final @Nullable List retryAspectExtList; private final FallbackDecorators fallbackDecorators; + private StringValueResolver embeddedValueResolver; /** * @param retryConfigurationProperties spring retry config properties @@ -108,11 +112,12 @@ public Object retryAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, io.github.resilience4j.retry.Retry retry = getOrCreateRetry(methodName, backend); Class returnType = method.getReturnType(); - if (StringUtils.isEmpty(retryAnnotation.fallbackMethod())) { + String fallbackMethodValue = ValueResolver.resolve(this.embeddedValueResolver, retryAnnotation.fallbackMethod()); + if (StringUtils.isEmpty(fallbackMethodValue)) { return proceed(proceedingJoinPoint, methodName, retry, returnType); } FallbackMethod fallbackMethod = FallbackMethod - .create(retryAnnotation.fallbackMethod(), method, proceedingJoinPoint.getArgs(), + .create(fallbackMethodValue, method, proceedingJoinPoint.getArgs(), proceedingJoinPoint.getTarget()); return fallbackDecorators.decorate(fallbackMethod, () -> proceed(proceedingJoinPoint, methodName, retry, returnType)).apply(); @@ -215,4 +220,8 @@ private void cleanup() { })); } + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } } diff --git a/resilience4j-spring/src/main/java/io/github/resilience4j/utils/ValueResolver.java b/resilience4j-spring/src/main/java/io/github/resilience4j/utils/ValueResolver.java new file mode 100644 index 0000000000..b3977dac77 --- /dev/null +++ b/resilience4j-spring/src/main/java/io/github/resilience4j/utils/ValueResolver.java @@ -0,0 +1,15 @@ +package io.github.resilience4j.utils; + +import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; + +public class ValueResolver { + + public static String resolve(StringValueResolver valueResolver, String value) { + if (StringUtils.hasText(value)) { + return valueResolver.resolveStringValue(value); + } + return value; + } + +} diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/BulkheadDummyService.java b/resilience4j-spring/src/test/java/io/github/resilience4j/BulkheadDummyService.java index 5ac7f8ff6b..6dcfae0d55 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/BulkheadDummyService.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/BulkheadDummyService.java @@ -77,4 +77,10 @@ public Maybe maybe() { public Flowable flowable() { return flowableError(); } + + @Override + @Bulkhead(name = BACKEND, fallbackMethod = "#{'recovery'}") + public String spelSync() { + return syncError(); + } } diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/CircuitBreakerDummyService.java b/resilience4j-spring/src/test/java/io/github/resilience4j/CircuitBreakerDummyService.java index fb4d88d0bf..1fb2c0e4ac 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/CircuitBreakerDummyService.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/CircuitBreakerDummyService.java @@ -76,4 +76,10 @@ public Maybe maybe() { public Flowable flowable() { return flowableError(); } + + @Override + @CircuitBreaker(name = BACKEND, fallbackMethod = "#{'recovery'}") + public String spelSync() { + return syncError(); + } } diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/RateLimiterDummyService.java b/resilience4j-spring/src/test/java/io/github/resilience4j/RateLimiterDummyService.java index 7b94fe519c..6f28c1388f 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/RateLimiterDummyService.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/RateLimiterDummyService.java @@ -76,4 +76,10 @@ public Maybe maybe() { public Flowable flowable() { return flowableError(); } + + @Override + @RateLimiter(name = BACKEND, fallbackMethod = "#{'recovery'}") + public String spelSync() { + return syncError(); + } } diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/RetryDummyService.java b/resilience4j-spring/src/test/java/io/github/resilience4j/RetryDummyService.java index f82a72cb15..bb6231a5d8 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/RetryDummyService.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/RetryDummyService.java @@ -76,4 +76,10 @@ public Maybe maybe() { public Flowable flowable() { return flowableError(); } + + @Override + @Retry(name = BACKEND, fallbackMethod = "#{'recovery'}") + public String spelSync() { + return syncError(); + } } diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/TestDummyService.java b/resilience4j-spring/src/test/java/io/github/resilience4j/TestDummyService.java index ab731474e1..b5e0edc5b1 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/TestDummyService.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/TestDummyService.java @@ -49,6 +49,8 @@ public interface TestDummyService { Flowable flowable(); + String spelSync(); + default String syncError() { throw new RuntimeException("Test"); } diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/bulkhead/configure/BulkheadRecoveryTest.java b/resilience4j-spring/src/test/java/io/github/resilience4j/bulkhead/configure/BulkheadRecoveryTest.java index d9f67d6887..596af687f0 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/bulkhead/configure/BulkheadRecoveryTest.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/bulkhead/configure/BulkheadRecoveryTest.java @@ -91,4 +91,9 @@ public void testMaybeRecovery() { public void testFlowableRecovery() { assertThat(testDummyService.flowable().blockingFirst()).isEqualTo("recovered"); } -} \ No newline at end of file + + @Test + public void testSpelRecovery() { + assertThat(testDummyService.spelSync()).isEqualTo("recovered"); + } +} diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerRecoveryTest.java b/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerRecoveryTest.java index 1e53a6c86f..b25292cf03 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerRecoveryTest.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/circuitbreaker/configure/CircuitBreakerRecoveryTest.java @@ -81,4 +81,9 @@ public void testMaybeRecovery() { public void testFlowableRecovery() { assertThat(testDummyService.flowable().blockingFirst()).isEqualTo("recovered"); } -} \ No newline at end of file + + @Test + public void testSpelRecovery() { + assertThat(testDummyService.spelSync()).isEqualTo("recovered"); + } +} diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/ratelimiter/configure/RateLimiterRecoveryTest.java b/resilience4j-spring/src/test/java/io/github/resilience4j/ratelimiter/configure/RateLimiterRecoveryTest.java index a35d072c16..b9f177bd56 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/ratelimiter/configure/RateLimiterRecoveryTest.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/ratelimiter/configure/RateLimiterRecoveryTest.java @@ -81,4 +81,9 @@ public void testMaybeRecovery() { public void testFlowableRecovery() { assertThat(testDummyService.flowable().blockingFirst()).isEqualTo("recovered"); } -} \ No newline at end of file + + @Test + public void testSpelRecovery() { + assertThat(testDummyService.spelSync()).isEqualTo("recovered"); + } +} diff --git a/resilience4j-spring/src/test/java/io/github/resilience4j/retry/configure/RetryRecoveryTest.java b/resilience4j-spring/src/test/java/io/github/resilience4j/retry/configure/RetryRecoveryTest.java index 2351170b1a..092de8f040 100644 --- a/resilience4j-spring/src/test/java/io/github/resilience4j/retry/configure/RetryRecoveryTest.java +++ b/resilience4j-spring/src/test/java/io/github/resilience4j/retry/configure/RetryRecoveryTest.java @@ -81,4 +81,10 @@ public void testMaybeRecovery() { public void testFlowableRecovery() { assertThat(testDummyService.flowable().blockingFirst()).isEqualTo("recovered"); } -} \ No newline at end of file + + @Test + public void testSpelRecovery() { + assertThat(testDummyService.spelSync()).isEqualTo("recovered"); + } + +}