@Configuration与Cglib的故事

业余时间将kafka与spring-boot框架集成造轮子的过程中遇到@Configuration注解的问题,与大家分享一下。
问题重现
=========
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 元数据,定义了消费者topic

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface KafkaMessageListener {
  String[] topics() default {};
}

定义consumerFactory实现InitializingBean,在afterPropertiesSet()方法中,扫描代码包中所有声明了@KafkaMessageListener注解的消费者,取得注解中的topic信息, 并根据这些信息创建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);
          }
        }
      }
    }
  }

运行过程中发现并没有按照预先设想的运行,debug之。发现一个非常猎奇的现象。
首先
====
在applicationContext.getBeanNamesForAnnotation方法确实返回了含有@KafkaMessageListener注解的beanName:topicConsumer;
但是topicConsumer.getClass()返回的类中居然一个注解都没有。
仔细观察applicationContext.getBean(listenerName)返回的类,类名为“topicConsumer$$EnhancerBySpringCGLIB$$**”,是一个由cglib生产的动态代理。与@Component的实现方式不同。
由于我声明的注解@KafkaMessageListener 不是@Inherited的,所以Cglib生成的类自然无法获得其父类的注解。
在臧老板的帮助下,在spring reference doc中找到了理论依据:
All @Configuration classes are subclassed at startup-time with CGLIB

对对对!藏在referenceDoc就好了,千万别写在javaDoc里让人发现了!!

其次

applicationContext.getBeanNamesForAnnotation(@Annotation.class)底层的实现使用了Spring的AnnotationUtils.findAnnotation(clazz, annotationType)方法,这个方法并不是简单的反射拿注解,而是递归的扫描每个类的所有父类中的注解。所以即使是父类包含此注解的动态代理也可以被找到。

解决办法

老老实实把@Configuration注解换成@Component就解决了这个问题。还是要加强学习

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,838评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,122评论 6 342
  • spring官方文档:http://docs.spring.io/spring/docs/current/spri...
    牛马风情阅读 5,719评论 0 3
  • 什么是Spring Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Jav...
    jemmm阅读 16,696评论 1 133
  • 今天内容介绍 Spring框架的IOC基于注解的方式 注解类型 注解生命周期 Spring框架整合JUnit单元测...

友情链接更多精彩内容