From 66f151ffe041c2fd2f1239d3d1e6857e46264a44 Mon Sep 17 00:00:00 2001 From: Dimitri Tugo Date: Sat, 26 Jul 2025 06:51:44 +0200 Subject: [PATCH] Fix: placeholders in includeNames/excludeNames 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 --- .../RetryableTopicAnnotationProcessor.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/spring-kafka/src/main/java/org/springframework/kafka/annotation/RetryableTopicAnnotationProcessor.java b/spring-kafka/src/main/java/org/springframework/kafka/annotation/RetryableTopicAnnotationProcessor.java index c090b579fb..d5d1274209 100644 --- a/spring-kafka/src/main/java/org/springframework/kafka/annotation/RetryableTopicAnnotationProcessor.java +++ b/spring-kafka/src/main/java/org/springframework/kafka/annotation/RetryableTopicAnnotationProcessor.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -132,10 +133,15 @@ public RetryTopicConfiguration processAnnotation(String[] topics, Class clazz if (resolvedTimeout != null) { timeout = resolvedTimeout; } - List> includes = resolveClasses(annotation.include(), annotation.includeNames(), + + String[] resolvedIncludeNames = resolveToStringArray(annotation.includeNames()); + List> includes = resolveClasses(annotation.include(), resolvedIncludeNames, "include"); - List> excludes = resolveClasses(annotation.exclude(), annotation.excludeNames(), + + String[] resolvedExcludeNames = resolveToStringArray(annotation.excludeNames()); + List> excludes = resolveClasses(annotation.exclude(), resolvedExcludeNames, "exclude"); + boolean traverse = false; if (StringUtils.hasText(annotation.traversingCauses())) { Boolean traverseResolved = resolveExpressionAsBoolean(annotation.traversingCauses(), "traversingCauses"); @@ -423,4 +429,25 @@ private String resolve(String value) { return value; } + private String[] resolveToStringArray(String[] values) { + List result = new ArrayList<>(); + for (String value : values) { + Object resolved = resolveExpression(value); + if (resolved instanceof String[] strings) { + Collections.addAll(result, strings); + } + else if (resolved instanceof Collection coll) { + for (Object item : coll) { + result.add(item.toString()); + } + } + else if (resolved instanceof String str) { + result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(str))); + } + else if (resolved != null) { + result.add(resolved.toString()); + } + } + return result.toArray(new String[0]); + } }