在春季的多个事件上使用@EventListener注释

这似乎是一个非常直接的问题,但我似乎无法在任何地方找到答案。

在Spring中,我可以使用@EventListener注释为事件创建一个侦听器,如下所示:

@Component
public class MyListener {

    @EventListener
    public void handleEvent(ContextRefreshedEvent event) {
        ...
    }
}

但是,如果我需要相同的方法来侦听多个事件并根据发生的事件以不同的方式执行操作,该怎么办?

直觉上,我在想类似这样的事情:

@Component
public class MyListener {

    @EventListener
    public void handleEvents(ContextRefreshedEvent event, ContextStopped event) {
         String event;
         if(event instanceof ContextRefreshedEvent)
              event = "Refreshed";
         if(event instanceof ContextStoppedEvent)
              event = "Stopped";
    }
}

EventListener 批注侦听多个事件的正确方法是什么,同一方法如何根据发生的实际事件进行区分?


答案 1

创建侦听多个事件的事件侦听器通常很容易:

@EventListener({EventA.class, EventB.class})
public doSomething() {
   ...
}

但显然,此方法不允许您访问基础事件。基于javadoc,它似乎不可能做你建议的事情EventListener

如果带批注的方法支持单个事件类型,则该方法可以声明反映要侦听的事件类型的单个参数。如果带批注的方法支持多个事件类型,则此批注可能使用 classes 属性引用一个或多个受支持的事件类型。有关更多详细信息,请参阅 classes() javadoc。

...

如果 () 属性是用单个值指定的,则带注释的方法可以选择性地接受单个参数。但是,如果此特性是使用多个值指定的,则带批注的方法不得声明任何参数。classes

因此,似乎没有任何机制来使用多个事件并根据这些事件的主体采取不同的操作。我建议这不应该是必要的,但你总是可以注册特定于事件的方法,然后让它们调用共享方法来执行任何常见功能。@EventListener

资料来源:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/event/EventListener.html


答案 2

从 Spring 4.2 开始,您可以在方法声明中使用子类来处理扩展此子类的所有事件:

@EventListener
public void handleEvent(ApplicationEvent event) {
  // listen all descendants of the ApplicationEvent
}

2020-10-07更新:
注意:下面的解决方案有效,但与官方文档(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/event/EventListener.html#classes--)相矛盾,并且可能会在下一个春季版本中停止工作。

此外,您还可以使用 annotion 的属性缩小事件列表的范围:

@EventListener({ContextRefreshedEvent.class, ApplicationReadyEvent.class})
public void handleEvent(Object event) {
  // listen only ContextRefreshedEvent and ApplicationReadyEvent
}