I'm using @KafkaListener and I need a dynamic topic name so I use the SpEL '__listener' in order to do that
@PostConstruct
public void init() {
myProps= generateTopicDynamically();
}
@KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(@Payload MyObject myObject) {
//Do something with my event
}
It works perfectly well.
The main issue is when I want to add another annotation that trigger some Aspect programmation
@MyCustomAnnotationToRecordPerformance @KafkaListener(topics = "#{__listener.myProps}") public void listenerKafka(@Payload MyObject myObject)
and here the aspect class
@Aspect
@Configuration
@Slf4j
public class MyCustomAnnotationToRecordPerformanceAspect {
@Pointcut("@annotation(MyCustomAnnotationToRecordPerformance)")
public void annotationMyCustomAnnotationToRecordPerformance() {
}
@Around("annotationMyCustomAnnotationToRecordPerformance()")
public Object doSomething(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
return proceedingJoinPoint.proceed();
}
}
I have this issue because Spring try to resolve __listener before @PostConstruct has been called.
Caused by: java.lang.IllegalArgumentException: @KafKaListener can't resolve 'null' as a String
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.resolveAsString(KafkaListenerAnnotationBeanPostProcessor.java:648)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.resolveTopics(KafkaListenerAnnotationBeanPostProcessor.java:520)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.processListener(KafkaListenerAnnotationBeanPostProcessor.java:419)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.processKafkaListener(KafkaListenerAnnotationBeanPostProcessor.java:370)
at org.springframework.kafka.annotation.KafkaListenerAnnotationBeanPostProcessor.postProcessAfterInitialization(KafkaListenerAnnotationBeanPostProcessor.java:298)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
... 41 common frames omitted
I tried to debug it
We can see lot of CGLIB reference, so bean has been already proxified, but all properties are null. So I supposed Autowired and PostConstruct method has not been called yet
For now, I tried to delay the processor that manage @KafkaListener, but I was not able to find where I can change that without have to redefine fully Kafka configuration
@EnableKafka import KafkaListenerConfigurationSelector that is DeferredImportSelector.
Here the comment on this class
A {@link DeferredImportSelector} implementation with the lowest order to import a {@link KafkaBootstrapConfiguration} as late as possible.
So I supposed it already delay as late as possible based on the comment
I test it with @Transactional, and I have the same issue.
@Transactional
@KafkaListener(topics = "#{__listener.myProps}")
public void listenerKafka(@Payload MyObject myObject)
Do have any idea about it?
The only alternative I see for now is split my class in 2 and create 2 beans. KafkaListener method call the other bean. But I found very strange to have to do that.
Thanks in advance for you help.