一、一个事件的封装、发布以及监听
事件类封装
把需要的信息封装到一个事件类中
@Data
public class Person {
private String name;
}
@Data
public class PersonEvent {
private Person person;
private String addOrUpdate;
public PersonEvent(Person person, String addOrUpdate) {
this.person = person;
this.addOrUpdate = addOrUpdate;
}
}
事件监听处理
@Component
public class EventListenerService {
@EventListener
public void handlePersonEvent(PersonEvent personEvent) {
System.out.println("handlePersonEvent 监听到 PersonEvent");
//处理逻辑
}
}
发布事件
@RestController
public class TestController {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
@GetMapping("/publishEvent")
public void publishEvent() {
applicationEventPublisher.publishEvent(new PersonEvent(new Person(), "add"));
}
}
以上就是一个事件的封装、监听以及发布的过程,但我们需要的事件多了以后,每一个对象都需要一个对应的 xxxxEvent 封装对象。这样的代码过于冗余。
二、使用泛型封装
事件对象
@Data
public class MyBaseEvent<T> {
private T data;
private String addOrUpdate;
public MyBaseEvent(T data, String addOrUpdate) {
this.data = data;
this.addOrUpdate = addOrUpdate;
}
}
事件监听
@Component
public class MyBaseEventListenerService {
@EventListener
public void handleMyEvent(MyBaseEvent<?> myBaseEvent){
Object data = myBaseEvent.getData();
if(data instanceof Person){
System.out.println("handleMyEvent 监听到 person");
}else if (data instanceof Order){
System.out.println("handleMyEvent 监听到 order");
}
}
}
事件发布
@Controller
@RequestMapping("/eventTest")
public class EventTestController {
@Resource
private ApplicationEventPublisher applicationEventPublisher;
@GetMapping("/publishEvent")
@ResponseBody
public void publishEvent() {
applicationEventPublisher.publishEvent(new MyBaseEvent(new Person(),"add"));
applicationEventPublisher.publishEvent(new MyBaseEvent(new Order(),"add"));
}
}
运行结果
结果是正常执行了,但是不同的类型监听的逻辑都在一个方法中了,如果事件的类型很多个的时候,这个监听的方法就会变的很繁琐,需要用非常多的 if 分支去做判断。
如果我们把监听拆开:
@Component
public class MyBaseEventListenerService {
@EventListener
public void handleMyEvent(MyBaseEvent<?> myBaseEvent){
Object data = myBaseEvent.getData();
if(data instanceof Person){
System.out.println("handleMyEvent 监听到 person");
}else if (data instanceof Order){
System.out.println("handleMyEvent 监听到 order");
}
}
@EventListener
public void handlePersonEvent(MyBaseEvent<Person> personEvent){
Object data = personEvent.getData();
System.out.println("handlePersonEvent 监听到 person");
}
@EventListener
public void handleOrderEvent(MyBaseEvent<Order> orderEvent){
Object data = orderEvent.getData();
System.out.println("handleOrderEvent 监听到 order");
}
}
但是再次重启服务,发起调用会发现控制台没有输出了,只执行了handleMyEvent这个监听器里的:
对于这种情况官方的说明是由于泛型擦除的原因,在运行时,Java 的泛型会被擦除,导致事件监听器无法正确地识别事件的泛型类型。例如 handlePersonEvent(MyBaseEvent<Person> personEvent)
中的 MyBaseEvent<Person>
会被擦除成为 MyBaseEvent
,因此,当你定义一个事件监听器方法时,参数类型为 MyBaseEvent<Person>
,在运行时会丢失泛型信息,参数类型会变成 MyBaseEvent
,而无法保留具体的泛型信息。这就会导致在事件发布时,虽然发布的是 MyBaseEvent<Person>
类型的事件,但在监听器方法中,参数类型已经丢失了泛型信息,从而导致了类型匹配问题,监听器无法正确地匹配到事件的具体类型,进而导致监听器未执行的情况发生。
官方提供了另一种实现方式:事件类实现 ResolvableTypeProvider
,重写 getResolvableType
方法,在运行期动态的获取泛型对应的真正的对象类型,从而解决了编译阶段泛型擦除的问题。
@Data
public class MyBaseEvent<T> implements ResolvableTypeProvider {
private T data;
private String addOrUpdate;
public MyBaseEvent(T data, String addOrUpdate) {
this.data = data;
this.addOrUpdate = addOrUpdate;
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getData()));
}
}
再次运行后:
可以看到拆开的监听器也正常执行了
原文: https://juejin.cn/post/7323793129710551080?utm_source=gold_browser_extension