ApplicationEvent 事件泛型封装记录

一、一个事件的封装、发布以及监听

事件类封装

把需要的信息封装到一个事件类中

@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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值