業(yè)余時間將kafka與spring-boot框架集成造輪子的過程中遇到@Configuration注解的問題,與大家分享一下。
問題重現(xiàn)
=========
TopicConsumer 類定義了一個Kafka消息的消費者,通過@Configuration的方式將類的初始化交給spring
@Configuration
@KafkaMessageListener(topics = "topic_a")
public class TopicConsumer implements MessageListener<String,String> {
@Override
public void onMessage(ConsumerRecord<String, String> consumerRecord) {
System.out.println(consumerRecord.value());
}
}
@KafkaMessageListener 注解中包含了一個topic 元數(shù)據(jù),定義了消費者topic
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface KafkaMessageListener {
String[] topics() default {};
}
定義consumerFactory實現(xiàn)InitializingBean,在afterPropertiesSet()方法中,掃描代碼包中所有聲明了@KafkaMessageListener注解的消費者,取得注解中的topic信息, 并根據(jù)這些信息創(chuàng)建kafka消費者,代碼如下:
public void afterPropertiesSet(){
for (String listenerName : applicationContext.getBeanNamesForAnnotation(KafkaMessageListener.class)) {
MessageListener listener = (MessageListener) applicationContext.getBean(listenerName);
Annotation[] annotations = listener.getClass().getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof KafkaMessageListener) {
for (String topic : ((KafkaMessageListener) annotation).topics()) {
dosomething(topic, listener);
}
}
}
}
}
運行過程中發(fā)現(xiàn)并沒有按照預(yù)先設(shè)想的運行,debug之。發(fā)現(xiàn)一個非常獵奇的現(xiàn)象。
首先
====
在applicationContext.getBeanNamesForAnnotation方法確實返回了含有@KafkaMessageListener注解的beanName:topicConsumer;
但是topicConsumer.getClass()返回的類中居然一個注解都沒有。
仔細觀察applicationContext.getBean(listenerName)返回的類,類名為“topicConsumer$$EnhancerBySpringCGLIB$$**”,是一個由cglib生產(chǎn)的動態(tài)代理。與@Component的實現(xiàn)方式不同。
由于我聲明的注解@KafkaMessageListener 不是@Inherited的,所以Cglib生成的類自然無法獲得其父類的注解。
在臧老板的幫助下,在spring reference doc中找到了理論依據(jù):
All @Configuration classes are subclassed at startup-time with CGLIB
對對對!藏在referenceDoc就好了,千萬別寫在javaDoc里讓人發(fā)現(xiàn)了??!
其次
applicationContext.getBeanNamesForAnnotation(@Annotation.class)底層的實現(xiàn)使用了Spring的AnnotationUtils.findAnnotation(clazz, annotationType)方法,這個方法并不是簡單的反射拿注解,而是遞歸的掃描每個類的所有父類中的注解。所以即使是父類包含此注解的動態(tài)代理也可以被找到。
解決辦法
老老實實把@Configuration注解換成@Component就解決了這個問題。還是要加強學(xué)習(xí)