diff --git a/README.md b/README.md index 9825fcf9..220fe373 100644 --- a/README.md +++ b/README.md @@ -464,13 +464,16 @@ public class Application { @Service class Service { - @Retryable(RemoteAccessException.class) + @Retryable(retryFor = RemoteAccessException.class, listeners = { "retryListener1", "retryListener2" }) public service() { // ... do something } } ``` +Starting with version 2.0.13, any bean instance of `RetryListener` are not considered as global listeners. +You always have to explicitly specify the `listeners` attribute in `@Retryable` if needed. + You can use the attributes of `@Retryable` to control the `RetryPolicy` and `BackoffPolicy`, as follows: ```java diff --git a/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java b/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java index 4dfa69d0..add856e3 100644 --- a/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java +++ b/src/main/java/org/springframework/retry/annotation/AnnotationAwareRetryOperationsInterceptor.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -77,6 +78,7 @@ * @author Gary Russell * @author Roman Akentev * @author Aftab Shaikh + * @author Jiandong Ma * @since 1.1 */ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionInterceptor, BeanFactoryAware { @@ -134,16 +136,6 @@ public void setNewItemIdentifier(NewMethodArgumentsIdentifier newMethodArguments this.newMethodArgumentsIdentifier = newMethodArgumentsIdentifier; } - /** - * Default retry listeners to apply to all operations. - * @param globalListeners the default listeners - */ - public void setListeners(Collection globalListeners) { - ArrayList retryListeners = new ArrayList<>(globalListeners); - AnnotationAwareOrderComparator.sort(retryListeners); - this.globalListeners = retryListeners.toArray(new RetryListener[0]); - } - @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; @@ -319,21 +311,17 @@ private RetryTemplate createTemplate(String[] listenersBeanNames) { if (listenersBeanNames.length > 0) { template.setListeners(getListenersBeans(listenersBeanNames)); } - else if (this.globalListeners != null) { - template.setListeners(this.globalListeners); - } return template; } private RetryListener[] getListenersBeans(String[] listenersBeanNames) { - if (listenersBeanNames.length == 1 && "".equals(listenersBeanNames[0].trim())) { - return new RetryListener[0]; - } - RetryListener[] listeners = new RetryListener[listenersBeanNames.length]; - for (int i = 0; i < listeners.length; i++) { - listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class); + List retryListeners = new ArrayList<>(listenersBeanNames.length); + for (String beanName : listenersBeanNames) { + if (StringUtils.hasText(beanName)) { + retryListeners.add(this.beanFactory.getBean(beanName, RetryListener.class)); + } } - return listeners; + return retryListeners.toArray(new RetryListener[0]); } private MethodInvocationRecoverer getRecoverer(Object target, Method method) { diff --git a/src/main/java/org/springframework/retry/annotation/CircuitBreaker.java b/src/main/java/org/springframework/retry/annotation/CircuitBreaker.java index 31fc5f1f..c3a13371 100644 --- a/src/main/java/org/springframework/retry/annotation/CircuitBreaker.java +++ b/src/main/java/org/springframework/retry/annotation/CircuitBreaker.java @@ -30,6 +30,7 @@ * @author Dave Syer * @author Artem Bilan * @author Gary Russell + * @author Jiandong Ma * @since 1.2 * */ @@ -199,4 +200,12 @@ @AliasFor(annotation = Retryable.class) String recover() default ""; + /** + * Bean names of retry listeners. + * @return retry listeners bean names + * @since 2.0.13 + */ + @AliasFor(annotation = Retryable.class) + String[] listeners() default {}; + } diff --git a/src/main/java/org/springframework/retry/annotation/RetryConfiguration.java b/src/main/java/org/springframework/retry/annotation/RetryConfiguration.java index 5bf0f0a5..fe61946a 100644 --- a/src/main/java/org/springframework/retry/annotation/RetryConfiguration.java +++ b/src/main/java/org/springframework/retry/annotation/RetryConfiguration.java @@ -70,6 +70,7 @@ * @author Gary Russell * @author Yanming Zhou * @author Evgeny Lazarev + * @author Jiandong Ma * @since 1.1 * */ @@ -77,7 +78,7 @@ @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Component public class RetryConfiguration extends AbstractPointcutAdvisor - implements IntroductionAdvisor, BeanFactoryAware, InitializingBean, SmartInitializingSingleton, ImportAware { + implements IntroductionAdvisor, BeanFactoryAware, InitializingBean, ImportAware { @Nullable protected AnnotationAttributes enableRetry; @@ -88,8 +89,6 @@ public class RetryConfiguration extends AbstractPointcutAdvisor private RetryContextCache retryContextCache; - private List retryListeners; - private MethodArgumentsKeyGenerator methodArgumentsKeyGenerator; private NewMethodArgumentsIdentifier newMethodArgumentsIdentifier; @@ -120,17 +119,8 @@ public void afterPropertiesSet() throws Exception { } } - @Override - public void afterSingletonsInstantiated() { - this.retryListeners = findBeans(RetryListener.class); - if (this.retryListeners != null) { - this.advice.setListeners(this.retryListeners); - } - } - private List findBeans(Class type) { - if (this.beanFactory instanceof ListableBeanFactory) { - ListableBeanFactory listable = (ListableBeanFactory) this.beanFactory; + if (this.beanFactory instanceof ListableBeanFactory listable) { if (listable.getBeanNamesForType(type).length > 0) { ArrayList list = new ArrayList<>(listable.getBeansOfType(type, false, false).values()); OrderComparator.sort(list); @@ -141,8 +131,7 @@ private List findBeans(Class type) { } private T findBean(Class type) { - if (this.beanFactory instanceof ListableBeanFactory) { - ListableBeanFactory listable = (ListableBeanFactory) this.beanFactory; + if (this.beanFactory instanceof ListableBeanFactory listable) { if (listable.getBeanNamesForType(type, false, false).length == 1) { return listable.getBean(type); } @@ -237,10 +226,9 @@ public boolean equals(Object other) { if (this == other) { return true; } - if (!(other instanceof AnnotationClassOrMethodPointcut)) { + if (!(other instanceof AnnotationClassOrMethodPointcut otherAdvisor)) { return false; } - AnnotationClassOrMethodPointcut otherAdvisor = (AnnotationClassOrMethodPointcut) other; return ObjectUtils.nullSafeEquals(this.methodResolver, otherAdvisor.methodResolver); } diff --git a/src/main/java/org/springframework/retry/annotation/Retryable.java b/src/main/java/org/springframework/retry/annotation/Retryable.java index d470b8ce..fd50e7b6 100644 --- a/src/main/java/org/springframework/retry/annotation/Retryable.java +++ b/src/main/java/org/springframework/retry/annotation/Retryable.java @@ -32,6 +32,7 @@ * @author Gary Russell * @author Maksim Kita * @author Roman Akentev + * @author Jiandong Ma * @since 1.1 * */ @@ -165,10 +166,7 @@ String exceptionExpression() default ""; /** - * Bean names of retry listeners to use instead of default ones defined in Spring - * context. If this attribute is set to an empty string {@code ""}, it will - * effectively exclude all retry listeners, including with the default listener beans, - * from being used. + * Bean names of retry listeners. * @return retry listeners bean names */ String[] listeners() default {}; diff --git a/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java b/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java index b0a81837..d56a793d 100644 --- a/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java +++ b/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java @@ -61,6 +61,7 @@ * @author Yanming Zhou * @author Anton Aharkau * @author Emanuele Ivaldi + * @author Jiandong Ma * @since 1.1 */ public class EnableRetryTests { @@ -77,7 +78,7 @@ public void vanilla() { TestConfiguration config = context.getBean(TestConfiguration.class); assertThat(config.listener1).isTrue(); assertThat(config.listener2).isTrue(); - assertThat(config.twoFirst).isTrue(); + assertThat(config.twoFirst).isFalse(); context.close(); } @@ -590,7 +591,7 @@ protected static class Service { private int count = 0; - @Retryable(RuntimeException.class) + @Retryable(retryFor = RuntimeException.class, listeners = { "listener1", "listener2" }) public void service() { if (this.count++ < 2) { throw new RuntimeException("Planned"); diff --git a/src/test/java/org/springframework/retry/annotation/EnableRetryWithListenersTests.java b/src/test/java/org/springframework/retry/annotation/EnableRetryWithListenersTests.java index 39ddfd2e..08d61ac1 100644 --- a/src/test/java/org/springframework/retry/annotation/EnableRetryWithListenersTests.java +++ b/src/test/java/org/springframework/retry/annotation/EnableRetryWithListenersTests.java @@ -166,7 +166,7 @@ protected static class Service { private int count = 0; - @Retryable(backoff = @Backoff(delay = 1000)) + @Retryable(backoff = @Backoff(delay = 1000), listeners = "listener") public void service() { if (count++ < 2) { throw new RuntimeException("Planned"); diff --git a/src/test/java/org/springframework/retry/stats/CircuitBreakerInterceptorStatisticsTests.java b/src/test/java/org/springframework/retry/stats/CircuitBreakerInterceptorStatisticsTests.java index b56dd127..bc948d86 100644 --- a/src/test/java/org/springframework/retry/stats/CircuitBreakerInterceptorStatisticsTests.java +++ b/src/test/java/org/springframework/retry/stats/CircuitBreakerInterceptorStatisticsTests.java @@ -35,6 +35,7 @@ /** * @author Dave Syer * @author Artem Bilan + * @author Jiandong Ma * */ public class CircuitBreakerInterceptorStatisticsTests { @@ -107,7 +108,7 @@ protected static class Service { private RetryContext status; - @CircuitBreaker(label = "test", maxAttempts = 1, recover = "recover") + @CircuitBreaker(label = "test", maxAttempts = 1, recover = "recover", listeners = "listener") public Object service(String input) throws Exception { this.status = RetrySynchronizationManager.getContext(); Integer attempts = (Integer) status.getAttribute("attempts"); diff --git a/src/test/java/org/springframework/retry/support/RetryMetricsTests.java b/src/test/java/org/springframework/retry/support/RetryMetricsTests.java index bb983530..02eb7ad7 100644 --- a/src/test/java/org/springframework/retry/support/RetryMetricsTests.java +++ b/src/test/java/org/springframework/retry/support/RetryMetricsTests.java @@ -133,19 +133,19 @@ protected static class Service { private int count = 0; - @Retryable + @Retryable(listeners = "metricsRetryListener") public void service1() { } - @Retryable + @Retryable(listeners = "metricsRetryListener") public void service2() { if (count++ < 2) { throw new RuntimeException("Planned"); } } - @Retryable + @Retryable(listeners = "metricsRetryListener") public void service3() { throw new RetryException("Planned"); }