Spring 通用事件的事件监听器

2 java generics spring event-listener

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

public class MyBeanClass <E> {
  @EventListener
  public void handleEvent(E event) { ... }
}
Run Code Online (Sandbox Code Playgroud)

配置:

public class MyConfig {
  @Bean
  public MyBeanClass<AEvent> myBeanAClass() {
    return new MyBeanClass<>();
  }
  @Bean
  public MyBeanClass<BEvent> myBeanBClass() {
    return new MyBeanClass<>();
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,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());
 }
Run Code Online (Sandbox Code Playgroud)

错误:

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)
Run Code Online (Sandbox Code Playgroud)

Eug*_*ene 5

由于类型擦除,E中的通用类型handleEvent(E event)将被替换为Object. 替换后的类将如下所示:

public class MyBeanClass {
  @EventListener
  public void handleEvent(Object event) { ... }
}
Run Code Online (Sandbox Code Playgroud)

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

解决方案 1.

为基本通用侦听器创建侦听器适配器 foreach 事件适配器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

活动:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

配置:

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

    @Bean
    public MyBeanBClass myBeanBClass() {
        return new MyBeanBClass(new MyBeanClass<>());
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案 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)
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

public class GenericSpringBEventListener {
    @EventListener
    public void handle(GenericSpringEvent<BEvent> event) {
        event.toString();
    }
}
Run Code Online (Sandbox Code Playgroud)

配置:

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

    @Bean
    public GenericSpringBEventListener genericSpringBEventListener() {
        return new GenericSpringBEventListener();
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)