Skip to content

Commit a94978a

Browse files
NipponSunrisespring-builds
authored andcommitted
GH-4016: Fix @RetryableTopic includeNames/excludeNames for SpEL
Fixes: #4016 Previously, the `includeNames` and `excludeNames` attributes of `@RetryableTopic` were not processed through the configured `BeanExpressionResolver` and `BeanExpressionContext`. This change ensures proper resolution from property placeholders `(${...})` and SpEL expressions `(#{...})`, consistent with the documentation and behavior of similar annotations like `@KafkaListener`. Signed-off-by: Dimitri Tugo <[email protected]> [[email protected]: Improve comint message] The Javadoc of the `@RetryableTopic` includes the statement: > All String properties can be resolved from property placeholders `${...}` or SpEL expressions `#{...}`. While `includeNames` and `excludeNames` are technically `String[]` rather than `String`, it’s a reasonable and common expectation that this applies to all string-valued attributes — including arrays. This interpretation is also consistent with how similar annotations behave, such as `@KafkaListener`, where array properties like topics support both property placeholders and SpEL expressions on a per-element basis. Enabling resolution here improves consistency with `@KafkaListener`, reduces potential surprises, and allows for cleaner externalization of retryable or non-retryable exception lists. Signed-off-by: Artem Bilan <[email protected]> (cherry picked from commit d4925d6)
1 parent fcdef5f commit a94978a

File tree

1 file changed

+29
-2
lines changed

1 file changed

+29
-2
lines changed

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.lang.reflect.Method;
2020
import java.util.ArrayList;
2121
import java.util.Arrays;
22+
import java.util.Collection;
2223
import java.util.Collections;
2324
import java.util.List;
2425
import java.util.Map;
@@ -131,10 +132,15 @@ public RetryTopicConfiguration processAnnotation(String[] topics, Class<?> clazz
131132
if (resolvedTimeout != null) {
132133
timeout = resolvedTimeout;
133134
}
134-
List<Class<? extends Throwable>> includes = resolveClasses(annotation.include(), annotation.includeNames(),
135+
136+
String[] resolvedIncludeNames = resolveToStringArray(annotation.includeNames());
137+
List<Class<? extends Throwable>> includes = resolveClasses(annotation.include(), resolvedIncludeNames,
135138
"include");
136-
List<Class<? extends Throwable>> excludes = resolveClasses(annotation.exclude(), annotation.excludeNames(),
139+
140+
String[] resolvedExcludeNames = resolveToStringArray(annotation.excludeNames());
141+
List<Class<? extends Throwable>> excludes = resolveClasses(annotation.exclude(), resolvedExcludeNames,
137142
"exclude");
143+
138144
boolean traverse = false;
139145
if (StringUtils.hasText(annotation.traversingCauses())) {
140146
Boolean traverseResolved = resolveExpressionAsBoolean(annotation.traversingCauses(), "traversingCauses");
@@ -422,4 +428,25 @@ private String resolve(String value) {
422428
return value;
423429
}
424430

431+
private String[] resolveToStringArray(String[] values) {
432+
List<String> result = new ArrayList<>();
433+
for (String value : values) {
434+
Object resolved = resolveExpression(value);
435+
if (resolved instanceof String[] strings) {
436+
Collections.addAll(result, strings);
437+
}
438+
else if (resolved instanceof Collection<?> coll) {
439+
for (Object item : coll) {
440+
result.add(item.toString());
441+
}
442+
}
443+
else if (resolved instanceof String str) {
444+
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(str)));
445+
}
446+
else if (resolved != null) {
447+
result.add(resolved.toString());
448+
}
449+
}
450+
return result.toArray(new String[0]);
451+
}
425452
}

0 commit comments

Comments
 (0)