java - Spring 通用事件的事件监听器

标签 java spring generics event-listener

我有两个同一类的 bean,我想分别监听一个特定于 bean 的通用事件:

public class MyBeanClass <E> {
  @EventListener
  public void handleEvent(E event) { ... }
}

配置:

public class MyConfig {
  @Bean
  public MyBeanClass<AEvent> myBeanAClass() {
    return new MyBeanClass<>();
  }
  @Bean
  public MyBeanClass<BEvent> myBeanBClass() {
    return new MyBeanClass<>();
  }
}

因此 bean“myBeanAClass”应监听 AEvent,而 bean“myBeanBClass”应监听 BEvent。

测试:

@Test
 public void testHandleAEvent() {
   AEvent event = new AEvent();
   publisher.publishEvent(event);
   Mockito.verify(myBeanAClass, times(1)).handleEvent(Mockito.any()); // Fail
   Mockito.verify(myBeanBClass, times(0)).handleEvent(Mockito.any());
 }

错误:

org.mockito.exceptions.verification.TooManyActualInvocations: 
mypackage.MyBeanClass#0 bean.handleEvent(
    <any>
);
Wanted 1 time:
-> at mypackage.MyTest.testHandleAEvent(MyTest.java:45)
But was 5 times:
-> at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-> at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-> at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-> at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
-> at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

最佳答案

因为Type Erasure通用类型EhandleEvent(E event)将替换为 Object 。替换后的类将如下所示:

public class MyBeanClass {
  @EventListener
  public void handleEvent(Object event) { ... }
}

这意味着此类监听器将接受来自应用程序的任何事件,甚至是由 spring 框架内部生成的事件。方法签名声明它消耗的事件类型。 EventListener documentation

解决方案 1. 为每个事件创建监听器适配器

基本通用监听器的适配器:

public class MyBeanClass <E> {
    public void handleEvent(E event) {
        event.toString();
    }
}

public class MyBeanAClass {
    private MyBeanClass<AEvent> myBeanClass;

    public MyBeanAClass(MyBeanClass<AEvent> myBeanClass) {
        this.myBeanClass = myBeanClass;
    }

    @EventListener
    public void handleEvent(AEvent event) {
        myBeanClass.handleEvent(event);
    }
}

public class MyBeanBClass {
    private MyBeanClass<BEvent> myBeanClass;

    public MyBeanBClass(MyBeanClass<BEvent> myBeanClass) {
        this.myBeanClass = myBeanClass;
    }

    @EventListener
    public void handleEvent(BEvent event) {
        myBeanClass.handleEvent(event);
    }
}

Activity :

public class AEvent extends ApplicationEvent {
    private final String message;

    public AEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
}

public class BEvent extends ApplicationEvent {
    private final String message;

    public BEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
}

配置:

public class MyConfig {
    @Bean
    public MyBeanAClass myBeanAClass() {
        return new MyBeanAClass(new MyBeanClass<>());
    }

    @Bean
    public MyBeanBClass myBeanBClass() {
        return new MyBeanBClass(new MyBeanClass<>());
    }
}

测试:

class ApplicationTests {
    @MockBean
    private MyBeanAClass myBeanAClass;

    @MockBean
    private MyBeanBClass myBeanBClass;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Test
    public void testHandleAEvent() {
        AEvent event = new AEvent(this, "Message");
        applicationEventPublisher.publishEvent(event);
        Mockito.verify(myBeanAClass, times(1)).handleEvent(Mockito.any());
        Mockito.verify(myBeanBClass, times(0)).handleEvent(Mockito.any());
    }
}

解决方案 2. 通用应用程序事件
创建通用事件类型。实现org.springframework.core.ResolvableTypeProvider在通用事件类中,然后监听器将解决它。

public class GenericSpringEvent<T> implements ResolvableTypeProvider {
    private final T source;

    public GenericSpringEvent(T source) {
        this.source = source;
    }

    public T getSource() {
        return source;
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(
                getClass(),
                ResolvableType.forInstance(this.source)
        );
    }
}

为每个事件实现通用监听器

public class GenericSpringAEventListener {
    @EventListener
    public void handle(GenericSpringEvent<AEvent> event) {
        event.toString();
    }
}

public class GenericSpringBEventListener {
    @EventListener
    public void handle(GenericSpringEvent<BEvent> event) {
        event.toString();
    }
}

配置:

public class MyConfig {
    @Bean
    public GenericSpringAEventListener genericSpringAEventListener() {
        return new GenericSpringAEventListener();
    }

    @Bean
    public GenericSpringBEventListener genericSpringBEventListener() {
        return new GenericSpringBEventListener();
    }
}

测试:

class ApplicationTests {
    @MockBean
    private GenericSpringAEventListener aListener;

    @MockBean
    private GenericSpringBEventListener bListener;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Test
    public void testHandleAEvent() {
        AEvent event = new AEvent(this, "Message");
        GenericSpringEvent<AEvent> genericEvent = new GenericSpringEvent<>(event);
        applicationEventPublisher.publishEvent(genericEvent);
        Mockito.verify(aListener, times(1)).handle(Mockito.any());
        Mockito.verify(bListener, times(0)).handle(Mockito.any());
    }
}

关于java - Spring 通用事件的事件监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71452445/

相关文章:

java - 当我只发送重定向时,如何在 Tomcat/Spring Boot 中关闭 JSESSIONID?

spring - Grails Ldap记住我IllegalArgumentException

java - 使用 commons-exec 执行复杂命令

java - 为什么我们在策略设计模式中需要上下文类?

java - 如何确保响应中存在某些 http header

swift - "Protocol ... can only be used as a generic constraint because it has Self or associated type requirements"是什么意思?

java - 流中的通用检查收集java

java - 抽象泛型类中的静态变量

java - 取消 Eclipse 插件作业

java - 从 swing jCombobox 迁移到 javaFX ComboBox