修改超类中的注释值,并使用新值动态实例化子类

时间:2018-06-21 13:51:39

标签: java spring spring-annotations spring-cloud-stream

我们正在使用Spring Cloud Stream作为我们基于微服务的体系结构中事件消息传递的基础实现。我们想更进一步,并在我们的服务和Spring Cloud Stream库之间提供一个抽象层,以允许动态频道订阅,而服务本身中没有太多样板配置代码。

最初的想法如下:

messaging-library提供了一个BaseHandler抽象类,所有单个服务都必须实现。特定服务的所有处理程序都希望使用相同的输入通道,尽管仅会调用与要处理的事件类型相对应的处理程序。如下所示:

public abstract class BaseEventHandler<T extends Event> {
    @StreamListener
    public abstract void handle(T event);
}

每个服务都提供自己的events程序包,其中包含N EventHandlers。有普通的POJO,必须以编程方式实例化。如下所示:

public class ServiceEventHandler extends BaseEventHandler<ImportantServiceEvent> {

    @Override
    public void handle(ImportantServiceEvent event) {
        // todo stuff
    }
}

请注意,这些是简单的类,目前还不是Spring bean,其中ImportantServiceEvent实现了Event

我们的messaging-library会在启动时尽早进行扫描,并执行处理程序初始化。为此,请完成以下步骤:

  • 我们扫描类路径中所有提供了某种事件处理功能的包,并检索BaseEventHandler的所有子类。
  • 我们在子类的层次结构中检索@StreamListener批注,并将其值更改为此服务的相应输入通道。
  • 由于我们的处理程序可能需要与某些其他应用程序组件(存储库等)进行对话,因此我们使用DefaultListableBeanFactory将我们的处理程序实例化为单例,如下所示:

    val bean = beanFactory.createBean(eventHandlerClass, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
    beanFactory.registerSingleton(eventHandlerClass.getSimpleName(), bean);
    

此后,我们遇到了几个问题。

Spring Cloud Stream @StreamListener注释是方法注释,因此无法继承。尽管如此,某种机制似乎仍能够在父级上找到它(因为StreamListenerAnnotationBeanPostProcessor已注册)并尝试在ServiceEventHandler初始化后执行后处理。我们的假设是Spring Cloud Stream使用类似AnnotationElementUtils.findAllMergedAnnotations()的东西。

因此,我们认为可能能够在子类的每个实例化之前更改基类的注释值。因此,我们认为,尽管我们的BaseEventHandler会简单地获得一个新值,然后在此初始化阶段结束时保持不变,但子类将在当时使用正确的通道名称​​实例化。实例化,因为我们不希望重新绑定。但是,事实并非如此,使用的@StreamListener批注的值始终是基数。

然后的问题是:Spring Cloud Stream我们想要什么?还是我们这里遇到的一个普通的Java问题(似乎并非如此)? Spring Cloud Stream团队是否预见到了这样的用例,我们只是做错了吗?

这个问题也发布在Spring Cloud Stream tracker上,以防可能引起更多关注。

1 个答案:

答案 0 :(得分:1)

由于同一个人监视SO和GitHub问题,因此在两个地方发布都毫无意义。对于问题,首选堆栈溢出。

您应该能够将BPP子类化;它特别具有以下扩展点:

/**
 * Extension point, allowing subclasses to customize the {@link StreamListener}
 * annotation detected by the postprocessor.
 *
 * @param originalAnnotation the original annotation
 * @param annotatedMethod the method on which the annotation has been found
 * @return the postprocessed {@link StreamListener} annotation
 */
protected StreamListener postProcessAnnotation(StreamListener originalAnnotation, Method annotatedMethod) {
    return originalAnnotation;
}

然后用您的变量覆盖bean定义

@Bean(name = STREAM_LISTENER_ANNOTATION_BEAN_POST_PROCESSOR_NAME)
public static StreamListenerAnnotationBeanPostProcessor streamListenerAnnotationBeanPostProcessor() {
    return new StreamListenerAnnotationBeanPostProcessor();
}