@Configuration与Cglib的故事

业余时间将kafka与spring-boot框架集成造轮子的过程中遇到@Configuration注解的问题,与大家分享一下。
问题重现
=========
TopicConsumer 类定义了一个Kafka消息的消费者,通过@Configuration的方式将类的初始化交给spring

@Configuration
@KafkaMessageListener(topics = "topic_a")
public class TopicConsumer implements MessageListener {
  @Override
  public void onMessage(ConsumerRecord 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就解决了这个问题。还是要加强学习

你可能感兴趣的:(@Configuration与Cglib的故事)