ApplicationEvent 事件泛型封装记录

news2024/9/20 21:12:02

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

事件类封装

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

@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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2150109.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【云安全】云服务安全攻防

一、云服务安全事件 1、CVE-2021-44228&#xff1a; AWS Log4Shell热补丁漏洞&#xff0c;用来进行容器逃逸和权限提升 2、CVE-2022-30137&#xff1a; Microsoft Azure Service Fabic权限提升漏洞&#xff0c;允许攻击者在容器内提升权限至主机节点root权限 FabricScape: Esca…

神奇的css动画:animation、transform、transition

前言 动画包括两个部分&#xff1a;描述动画的样式和用于指定动画开始、结束以及中间点样式的关键帧。 相比较于传统的脚本实现动画技术&#xff0c;使用css动画三个主要优点: 1.能够非常容易创建简单动画&#xff0c;甚至不需要了解JavaScript就能创建动画 2.动画运行效果…

Trainer API训练属于自己行业的本地大语言模型 医疗本地问答大模型示例

Trainer API 是 Hugging Face transformers 库中强大而灵活的工具&#xff0c;简化了深度学习模型的训练和评估过程。通过提供高层次的接口和多种功能&#xff0c;Trainer API 使研究人员和开发者能够更快地构建和优化自然语言处理模型 文章目录 前言一、Trainer API它能做什么…

Machine Learning Specialization 学习笔记(3)

文章目录 前言一、神经网络基本概念基本组成工作流程训练过程类型应用举例不同层次特征的学习 为什么从基础特征到复杂特征逐渐推进什么是感受野更简单的解释具体示例总结 二、TensorFlow实现简单神经网络安装及环境配置数据预处理标准化 Dense层Convolutional Layer训练DEBUG …

独立站技能树/工具箱1.0 总纲篇丨出海笔记

正所谓要把一件事做到90分很难&#xff0c;但做到60分基本上照着SOP做到位都没问题&#xff0c;如果我们能把每件事都做到60分&#xff0c;那绝对比至少60%的人都强&#xff0c;除非你的对手不讲武德——那就是他很可能看了我这篇文章&#xff0c;不但每方面都超过及格线&#…

MySQL高阶1853-转换日期格式

目录 题目 准备数据 分析数据 总结 题目 给定一个Days表&#xff0c;请你编写SQL查询语句&#xff0c;将Days表中的每一个日期转化为"day_name, month_name day, year"格式的字符串。 返回的结果表 不计顺序 。 准备数据 Create table If Not Exists Days (d…

Arthas 全攻略:让调试变得简单

文章目录 一、简介二、命令列表 一、简介 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff…

排序---冒泡排序、堆排序

一、冒泡排序 相邻两个位置交换&#xff0c;假设排升序&#xff0c;就不断把最大的往后拿&#xff0c;所以这段序列从后往前变得有序。 //flag为0&#xff0c;即这个数组已经是有序的了&#xff0c;节省循环次数 二、堆排序&#xff08;数组实现&#xff09; 具体原理介绍看这…

jetcache-阿里多级缓存框架神器一定要掌握

文章目录 1. 简介2. springboot集成jetcache2.1 引入依赖2.2 配置文件2.3 高级API模式&#xff1a;通过CacheManager使用缓存&#xff0c;2.7 版本才可使用2.4 &#xff08;推荐&#xff09;AOP模式&#xff1a;通过Cached,CacheUpdate,CacheInvalidate注解 1. 简介 JetCache是…

局部整体(六)利用python绘制树状图

局部整体&#xff08;六&#xff09;利用python绘制树状图 树状图&#xff08; Dendrogram&#xff09;简介 由一个根节点组成&#xff0c;根节点产生多个通过分支连接的子节点。常用于表示层次结构或显示聚类算法的结果。树状图既可以看明白数据的层次结构&#xff0c;也能明…

兴业小知识|法拍房你不知道的省钱小技巧~划走可就亏大了

如果说二手房市场是买卖双方之间的博弈&#xff0c;那法拍房市场则是纯买方的心理游戏。 在法拍房竞拍过程中&#xff0c;有人稳如泰山&#xff0c;有人坐立不安&#xff0c;每一次的出价都是对相互底线的一番试探。 有激进竞拍&#xff0c;拍出天价的&#xff0c;有一举夺魁…

2024.9.20营养小题【2】(动态分配二维数组)

这道题里边涉及到了动态分配二维数组的知识点&#xff0c;不刷这道题我也不知道这个知识点&#xff0c;算是一个比较进阶一点的知识点了。 参考&#xff1a;C语言程序设计_动态分配二维数组_哔哩哔哩_bilibili【C/C 数据结构 】二维数组结构解析 - 知乎 (zhihu.com)

网络爬虫Request静态页面数据获取

在现代 Web 开发中,HTTP 请求(Request)是与服务器进行通信的核心操作。无论是在前端还是后端开发中,数据的获取、传递以及处理都离不开请求的应用。特别是在静态页面的数据获取中,使用请求可以将页面变得更加动态和互动,从而大大提升用户体验,使得页面内容更加丰富和灵活…

电风扇制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

电风扇正悄然成为制造业数字化转型浪潮中的一颗璀璨新星。通过构建5G智能工厂物联数字孪生平台&#xff0c;电风扇制造业正以前所未有的速度和精度&#xff0c;推进着整个行业的智能化、网络化与个性化发展。5G技术的飞速发展&#xff0c;为制造业带来了前所未有的通信速度和低…

vue 入门一

参考&#xff1a;丁丁的哔哩哔哩 1.使用vue 1.1 使用CDN的方式使用Vue mount和<div id"counter">关联起来 1.2 vue中的createApp import { createApp } from "vue"; import App from "./App.vue"; createApp(App).mount("#app&qu…

【软件测试】如何设计测试用例? 设计测试用例常用的方法.

目录 一.什么是测试用例?二.总体设计测试用例的万能公式.2.1 功能性能界面兼容易用安全2.2 弱网测试2.3 安装卸载测试. 三. 常用设计具体测试用例的方法3.1 等价类3.2 边界值3.3 正交法3.3.1 正交表3.3.2 如何设计正交表,并根据正交表编写测试用例 3.4 判定表法3.4.1 根据判定…

红日药业携手实在智能,构建RPA数字员工平台满足业务一体化需求 | 实在RPA案例

近日&#xff0c;天津红日药业股份有限公司&#xff08;简称“红日药业”&#xff09;与实在智能达成合作&#xff0c;依托实在智能业内领先的AIRPA技术&#xff0c;红日药业着手构建企业数字员工平台&#xff0c;满足业务一体化需求&#xff0c;培育新质生产力&#xff0c;为企…

基于PHP的电脑线上销售系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于phpMySQL的电脑线上销售系…

小程序服务零工市场

零工市场小程序有着信息发布、岗位匹配、线上接单、零工人员保障险参保、技能培训、费用结算、完工确认、服务评价、纠纷调解等功能&#xff0c;为求职者和雇主搭建一座高效、便捷、精准的对接桥梁。 用工单位通过小程序的“雇主找人”&#xff0c;发布招聘信息&#xff0c;找到…

react-native连接android原生模块

目录 搭建react-native项目 搭建node和jdk的环境 搭建Android的环境 创建React-native项目 运行react-native项目 下载夜神模拟器 使用adb连接夜神浏览器。 运行react-native项目 编写原生安卓的apk android studio中编写原生代码 在React-native编写代码。 搭建rea…