@Configuration與Cglib的故事

業(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í)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,273評論 6 342
  • spring官方文檔:http://docs.spring.io/spring/docs/current/spri...
    牛馬風(fēng)情閱讀 1,856評論 0 3
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,772評論 1 133
  • 今天內(nèi)容介紹 Spring框架的IOC基于注解的方式 注解類型 注解生命周期 Spring框架整合JUnit單元測...

友情鏈接更多精彩內(nèi)容