spring-projects / spring-projects/spring-kafka
Lambda or method-reference Converter beans break @KafkaListener startup
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 2.5k
- Forks
- 1.8k
- Avg merge
- 21h 49m
- Merged PRs (30d)
- 47
Description
In what version(s) of Spring for Apache Kafka are you seeing this issue?
4.0.6 (also present on main). Reproduced with Spring Boot 4.0.7 / Spring Framework 7.0.8 / Java 25.
Describe the bug
A Converter bean declared as a lambda or method reference makes application startup fail as soon as one @KafkaListener is present:
java.lang.IllegalArgumentException: Unable to determine source type <S> and target type <T> for your Converter [com.example.repro.ReproApplication$$Lambda/0x0000100001262c48]; does the class parameterize those types?
at org.springframework.core.convert.support.GenericConversionService.addConverter(GenericConversionService.java:92)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.addFormatters(KafkaListenerAnnotationBeanPostProcessor.java:1180)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.afterSingletonsInstantiated(KafkaListenerAnnotationBeanPostProcessor.java:347)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1147)
KafkaListenerAnnotationBeanPostProcessor.addFormatters collects every Converter bean in the application context and passes each one to the single-argument FormatterRegistry.addConverter(Converter) overload:
private void addFormatters(FormatterRegistry registry) {
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
registry.addConverter(converter);
}
...
}
That overload resolves S and T reflectively from the converter's class. The compiler does not emit a generic signature for the synthetic class of a lambda or method reference, so resolution always fails (Spring Framework's position on this is spring-projects/spring-framework#22509 — a language limitation, not a framework bug).
The important part: the same bean is perfectly valid everywhere else in Spring Boot. ApplicationConversionService.addBeans wraps converter beans in a ConverterBeanAdapter built from the bean definition's ResolvableType (the @Bean method signature), which does carry the generics — see spring-projects/spring-boot#22885. So a lambda Converter bean works for MVC data binding, @ConfigurationProperties conversion, etc., and only breaks when @KafkaListener enters the picture. Nothing in the documentation suggests Converter beans must be named classes, and the failure surfaces in an unrelated part of the application — a shared library can ship such a bean and break only those consumers that happen to use Kafka.
To Reproduce
Two files, no broker needed — it fails during context refresh.
pom.xml (Boot 4.0.7 parent):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-kafka</artifactId>
</dependency>
@SpringBootApplication
public class ReproApplication {
public static void main(String[] args) {
SpringApplication.run(ReproApplication.class, args);
}
// Accepted by Boot everywhere else; breaks @KafkaListener processing.
@Bean
public Converter<String, LocalDate> localDateConverter() {
return LocalDate::parse;
}
@KafkaListener(topics = "some-topic", groupId = "repro")
public void listen(String payload) {}
}
Startup fails with the stack trace above. Replacing the lambda with an anonymous inner class (new Converter<String, LocalDate>() { ... }) makes it start.
Expected behavior
@KafkaListener processing should tolerate lambda/method-reference Converter beans, the way the rest of Boot does — or, failing that, skip a converter it cannot introspect instead of aborting the whole context.
Suggested fix, mirroring spring-boot#22885: resolve the converter's generics from the bean definition's ResolvableType and register it through the GenericConverter route (ConverterBeanAdapter-style) rather than the reflection-based single-arg overload.
Workaround
Declare such beans as named or anonymous classes, or add a KafkaListenerConfigurer that supplies a custom MessageHandlerMethodFactory (which bypasses addFormatters entirely).
Sample
The two files above are the complete sample; happy to push it to a repository if that is preferred.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the two-file sample with the Boot 4.0.7 parent, then inspect KafkaListenerAnnotationBeanPostProcessor.addFormatters and its FormatterRegistry registration path. Compare it with Spring Boot's ApplicationConversionService.addBeans and ConverterBeanAdapter handling of bean-definition ResolvableType. Done means a lambda or method-reference Converter bean no longer aborts context startup when an @KafkaListener is present.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, kafka, spring
- Domain
- backend, distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100