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

### Java使用泛型封装静态方法 为了在Java使用泛型封装静态方法,必须将静态方法本身定义为泛型方法。这意味着要在方法签名中的返回类型前声明类型参数列表 `<T>`。即使静态方法想要利用类级别的泛型参数,也需要这样做,因为静态上下文中无法访问实例化的类型参数。 #### 示例代码展示 下面是一个简单的例子,展示了如何创建一个接受任意类型的数组并打印其内容的泛型静态方法: ```java public class ArrayPrinter { /** * 此处定义了一个名为printArray的泛型静态方法, * 可以接收任何实现了Cloneable接口的对象数组作为输入,并遍历打印这些对象。 */ public static <T> void printArray(T[] inputArray) { for (T element : inputArray) { System.out.printf("%s ", element); } System.out.println(); } // 测试泛型静态方法的功能 public static void main(String[] args) { Integer[] intArray = {1, 2, 3}; String[] stringArray = {"Hello", "World"}; printArray(intArray); // 输出整数数组的内容 printArray(stringArray); // 输出字符串数组的内容 } } ``` 在这个例子中,`<T>` 表示此方法可以操作任意的数据类型 `T`,只要传入的是该类型的数组即可正常工作[^1]。 当涉及到更复杂的场景时,比如需要指定泛型的实际类型范围,则可以通过设置上界或下界的约束条件来实现更加灵活的操作。例如,上述代码片段中的注释提到的方法能够处理实现了特定接口(如 `Cloneable` 接口)的对象集合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值