Skip to content

Commit 67e0ed1

Browse files
authored
Add meta-annotation support to RetryableTopic (#2440)
* Add meta-annotation support to RetryableTopic * Add authorship to javadocs
1 parent a9cebb0 commit 67e0ed1

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

spring-kafka/src/main/java/org/springframework/kafka/annotation/RetryableTopic.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018-2021 the original author or authors.
2+
* Copyright 2018-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,11 +37,12 @@
3737
*
3838
* @author Tomaz Fernandes
3939
* @author Gary Russell
40+
* @author Fabio da Silva Jr.
4041
* @since 2.7
4142
*
4243
* @see org.springframework.kafka.retrytopic.RetryTopicConfigurer
4344
*/
44-
@Target({ ElementType.METHOD })
45+
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
4546
@Retention(RetentionPolicy.RUNTIME)
4647
@Documented
4748
public @interface RetryableTopic {

spring-kafka/src/main/java/org/springframework/kafka/retrytopic/RetryTopicConfigurer.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
*
139139
* <p>The other, non-exclusive way to configure the endpoints is through the convenient
140140
* {@link org.springframework.kafka.annotation.RetryableTopic} annotation, that can be placed on any
141-
* {@link org.springframework.kafka.annotation.KafkaListener} annotated methods, such as:
141+
* {@link org.springframework.kafka.annotation.KafkaListener} annotated methods, directly, such as:
142142
*
143143
* <pre>
144144
* <code>@RetryableTopic(attempts = 3,
@@ -148,6 +148,18 @@
148148
* // ... message processing
149149
* }</code>
150150
*</pre>
151+
* <p> Or through meta-annotations, such as:
152+
* <pre>
153+
* <code>@RetryableTopic(attempts = 3,
154+
* backoff = @Backoff(delay = 700, maxDelay = 12000, multiplier = 3))</code>
155+
* <code>public @interface WithExponentialBackoffRetry { }</code>
156+
*
157+
* <code>@WithExponentialBackoffRetry</code>
158+
* <code>@KafkaListener(topics = "my-annotated-topic")
159+
* public void processMessage(MyPojo message) {
160+
* // ... message processing
161+
* }</code>
162+
*</pre>
151163
* <p> The same configurations are available in the annotation and the builder approaches, and both can be
152164
* used concurrently. In case the same method / topic can be handled by both, the annotation takes precedence.
153165
*
@@ -192,6 +204,7 @@
192204
* If no DLT handler is provided, the default {@link LoggingDltListenerHandlerMethod} is used.
193205
*
194206
* @author Tomaz Fernandes
207+
* @author Fabio da Silva Jr.
195208
* @since 2.7
196209
*
197210
* @see RetryTopicConfigurationBuilder

spring-kafka/src/test/java/org/springframework/kafka/retrytopic/RetryTopicConfigurationProviderTests.java

+31
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
import static org.mockito.Mockito.mock;
2626
import static org.mockito.Mockito.times;
2727

28+
import java.lang.annotation.ElementType;
29+
import java.lang.annotation.Retention;
30+
import java.lang.annotation.RetentionPolicy;
31+
import java.lang.annotation.Target;
2832
import java.lang.reflect.Method;
2933
import java.util.Collections;
3034

@@ -41,6 +45,7 @@
4145
/**
4246
* @author Tomaz Fernandes
4347
* @author Gary Russell
48+
* @author Fabio da Silva Jr.
4449
* @since 2.7
4550
*/
4651
@ExtendWith(MockitoExtension.class)
@@ -61,6 +66,8 @@ class RetryTopicConfigurationProviderTests {
6166

6267
private final Method nonAnnotatedMethod = getAnnotatedMethod("nonAnnotatedMethod");
6368

69+
private final Method metaAnnotatedMethod = getAnnotatedMethod("metaAnnotatedMethod");
70+
6471
private Method getAnnotatedMethod(String methodName) {
6572
try {
6673
return this.getClass().getDeclaredMethod(methodName);
@@ -136,6 +143,19 @@ void shouldFindNone() {
136143

137144
}
138145

146+
@Test
147+
void shouldProvideFromMetaAnnotation() {
148+
149+
// setup
150+
willReturn(kafkaOperations).given(beanFactory).getBean("retryTopicDefaultKafkaTemplate", KafkaOperations.class);
151+
152+
// given
153+
RetryTopicConfigurationProvider provider = new RetryTopicConfigurationProvider(beanFactory);
154+
RetryTopicConfiguration configuration = provider.findRetryConfigurationFor(topics, metaAnnotatedMethod, bean);
155+
156+
// then
157+
then(this.beanFactory).should(times(0)).getBeansOfType(RetryTopicConfiguration.class);
158+
}
139159

140160
@Test
141161
void shouldNotConfigureIfBeanFactoryNull() {
@@ -157,4 +177,15 @@ public void annotatedMethod() {
157177
public void nonAnnotatedMethod() {
158178
// NoOps
159179
}
180+
181+
@Target({ElementType.METHOD})
182+
@Retention(RetentionPolicy.RUNTIME)
183+
@RetryableTopic
184+
static @interface MetaAnnotatedRetryableTopic {
185+
}
186+
187+
@MetaAnnotatedRetryableTopic
188+
public void metaAnnotatedMethod() {
189+
// NoOps
190+
}
160191
}

0 commit comments

Comments
 (0)