千家信息网

Spring事件发布与监听怎么实现

发表于:2024-11-17 作者:千家信息网编辑
千家信息网最后更新 2024年11月17日,这篇文章主要讲解了"Spring事件发布与监听怎么实现",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Spring事件发布与监听怎么实现"吧!一、事件监
千家信息网最后更新 2024年11月17日Spring事件发布与监听怎么实现

这篇文章主要讲解了"Spring事件发布与监听怎么实现",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Spring事件发布与监听怎么实现"吧!

  一、事件监听相关概念介绍

  1、流程分析

  事件:做了什么事。例如,我在写博客,写博客就是一个事件。

  监听器:监听发生事件的组件。例如,我们日常生活中的火灾报警器,监听有没有发生火灾事件。

  在一个完整的事件体系中,除了事件和监听器以外,还应该有3个概念;

  1. 事件源:事件的产生者,任何一个event都必须有一个事件源;

  2. 事件广播器:它是事件和事件监听器之间的桥梁,负责把事件通知给事件监听器;

  3. 事件监听器注册表:就是spring框架为所有的监听器提供了一个存放的地方;

  通过流程图,可以看出它们是如何各司其职的,如下:

  其实通过流程图,我们很容易发现事件体系就是观察者模式的具体实现,它并没有任何的神秘之处。

  2、流程分析

  结构分析:

  1. 事件类(ApplicaitonEvent):目前spring框架本身仅仅提供了几个事件,很多的事件都是需要自定义的。

  ApplicationEvent唯一的构造函数是ApplicaitonEvent(Object source),通过source指定事件源。 它有两个子类;

  (1)ApplicationContextEvent:容器事件,也就是说事件源是ApplicationContext,框架提供了四个子类,分别代表容器启动,刷新,停止和关闭事件。

  (2)RequestHandleEvent:这是一个与Web应用相关的事件,当一个请求被处理后,才会产生该事件。

  一般来说,我们都是扩展ApplicationEvent来自定义事件。下面会有栗子。

  2. 事件监听器接口(ApplicationListener)

  所有的监听器都需要实现该接口,该接口只定义了一个方法:onApplicaitonEvent (E event),该方法接收事件对象,在该方法中编写事件的响应处理逻辑。

  二、手写模拟事件发布与监听

  注:想直接了解Spring事件监听与发布的,可以跳过这节,但是我建议你还是看一下。

  需求:

  假设现在公司让你开发一个文件操作帮助类 ,

  定义一个文件读写方法 读写某个文件 写到某个类里面去 //但是 有时候可能会需要记录文件读取进度条的需求

  有时候需要进度条 如何实现?

  答案:我们可以采用事件发布与监听。

  事件:文件上传

  事件源:事件在哪里发布的,比如说我们在A类中,发布了事件。那么A类的对象就是事件源。

  监听器:我们编写的FileUploadListener对这个事件进行了监听。并在监听到了当前事件之后,发布事件。

  代码编写:

  /**

  * @ClassName ApplicationEvent

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 20:29

  */

  public class ApplicationEvent {

  }

  /**

  * @ClassName ApplicationListener

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 20:29

  */

  public interface ApplicationListener {

  void onEvent(E e);

  }

  /**

  * @ClassName ListenerManage

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 20:44

  */

  //事件管理器

  public class ListenerManage {

  //保存所有的监听器

  static List> list = new ArrayList<>();

  //添加监听器 注:如果要做的更加优雅,应该做成扫描全局,通过扫描将所有的监听器放入管理器的容器列表,这里为了方便演示就不做复杂了。

  //springboot是从spring的BeanFactory中获取listener

  public static void addListener(ApplicationListener listener) {

  list.add(listener);

  }

  //判断一下 有哪些监听器 监听了这个事件

  public static void publishEvent(ApplicationEvent event) {

  for (ApplicationListener applicationListener : list) {

  //获取ApplicationListener的泛型

  Class typeParameter = (Class) ((ParameterizedType) applicationListener.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];

  if (typeParameter.equals(event.getClass())) {

  applicationListener.onEvent(event);

  }

  }

  }

  }

  /**

  * @ClassName FileUploadEvent

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 21:37

  */

  public class FileUploadEvent extends ApplicationEvent {

  private int fileSize;

  private int readSize;

  public FileUploadEvent(int fileSize, int readSize) {

  this.fileSize = fileSize;

  this.readSize = readSize;

  }

  public int getFileSize() {

  return fileSize;

  }

  public void setFileSize(int fileSize) {

  this.fileSize = fileSize;

  }

  public int getReadSize() {

  return readSize;

  }

  public void setReadSize(int readSize) {

  this.readSize = readSize;

  }

  }

  /**

  * @ClassName FileUploadListener

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 21:38

  */

  public class FileUploadListener implements ApplicationListener {

  @Override

  public void onEvent(FileUploadEvent fileUploadEvent) {

  double molecule = fileUploadEvent.getFileSize();

  double denominator = fileUploadEvent.getReadSize();

  System.out.println("当前文件上传进度百分比:" + (denominator / molecule * 100 + "%"));

  }

  }

  /**

  * @ClassName FileUtil

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 17:06

  */

  public class FileUtil {

  public static int READ_SIZE = 100;

  public static void fileWrite(InputStream is, OutputStream os) throws Exception {

  fileWrite(is, os, null);

  }

  public static void fileWrite(InputStream is, OutputStream os, FileListener fileListener) throws Exception {

  BufferedInputStream bis = new BufferedInputStream(is);

  BufferedOutputStream bos = new BufferedOutputStream(os);

  /**

  * 如果是网络请求最好不要用这个方法拿fileSize,因为这个方法会产生阻塞。最好传一个File对象进来。

  * 这里作为演示,就不去处理细节了。

  */

  //文件总大小

  int fileSize = is.available();

  //一共读取了多少

  int readSize = 0;

  byte[] readedBytes = new byte[READ_SIZE];

  //控制是否退出

  boolean exit = true;

  while (exit) {

  //文件小于第一次读的大小的时候

  if (fileSize < READ_SIZE) {

  byte[] fileBytes = new byte[fileSize];

  //将缓冲区中的数据写入到字节数组fileBytes中

  bis.read(fileBytes);

  //向文件写入fileBytes数组的内容

  bos.write(fileBytes);

  readSize = fileSize;

  exit = false;

  //当你是最后一次读的时候

  } else if (fileSize < readSize + READ_SIZE) {

  byte[] bytes = new byte[fileSize - readSize];

  readSize = fileSize;

  bis.read(bytes);

  bos.write(bytes);

  exit = false;

  } else {

  bis.read(readedBytes);

  readSize += READ_SIZE;

  bos.write(readedBytes);

  }

  //发布事件

  ListenerManage.publishEvent(new FileUploadEvent(fileSize, readSize));

  if (fileListener != null) {

  fileListener.updateLoad(fileSize, readSize);

  }

  }

  bis.close();

  bos.close();

  }

  }

  /**

  * @ClassName FileReadTest

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/9 18:26

  */

  public class FileReadTest {

  public static void main(String[] args) throws Exception {

  ListenerManage.addListener(new FileUploadListener());

  //这里根据实际情况去设置读写的文件

  File file = new File("F:\\测试写出.txt");

  if (!file.exists()) {

  file.createNewFile();

  }

  //如果需要做进度条功能,再添加一个fileListener参数

  fileWrite(new FileInputStream(new File("F:\\明天要做的事.txt")), new FileOutputStream(file));

  }

  }

  运行结果:

  当前文件上传进度百分比:14.245014245014245%

  当前文件上传进度百分比:28.49002849002849%

  当前文件上传进度百分比:42.73504273504273%

  当前文件上传进度百分比:56.98005698005698%

  当前文件上传进度百分比:71.22507122507122%

  当前文件上传进度百分比:85.47008547008546%

  当前文件上传进度百分比:99.71509971509973%

  当前文件上传进度百分比:100.0%

  三、Spring的时间发布与监听

  我们在上面手动模拟了Spring的时间发布与监听后,看如果上面的例子后,我们使用Spring再写一个事件发布与监听的例子。郑州人流医院 http://rl.zyfuke.com/

  package com.evan.spring.config;

  import org.springframework.context.annotation.ComponentScan;

  /**

  * @ClassName Appconfig

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/10 16:04

  */

  @ComponentScan("com")

  public class AppConfig {

  }

  package com.evan.spring.event;

  import org.springframework.context.ApplicationContext;

  import org.springframework.context.event.ApplicationContextEvent;

  import org.springframework.context.event.ContextStartedEvent;

  /**

  * @ClassName MyEvent

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/10 15:39

  */

  public class WriteBlogEvent extends ApplicationContextEvent {

  String name;

  String address;

  public WriteBlogEvent(ApplicationContext source, String name, String address) {

  super(source);

  this.name = name;

  this.address = address;

  }

  public String getName() {

  return name;

  }

  public String getAddress() {

  return address;

  }

  }

  Spring的事件监听可以基于注解或实现接口。对于同一个事件,如果两个都存在,相当于多个监听器监听一个事件。

  两个监听器内的方法都会执行。

  package com.evan.spring.listener;

  import com.evan.spring.event.WriteBlogEvent;

  import org.springframework.context.ApplicationListener;

  import org.springframework.stereotype.Component;

  /**

  * @ClassName WriteBlogListener

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/10 15:47

  */

  @Component

  public class WriteBlogListener implements ApplicationListener {

  @Override

  public void onApplicationEvent(WriteBlogEvent writeBlogEvent) {

  String name = writeBlogEvent.getName();

  String address = writeBlogEvent.getAddress();

  System.out.println("基于实现接口:" + name + "在" + address + "写了一篇博客");

  }

  }

  package com.evan.spring.listener;

  import com.evan.spring.event.WriteBlogEvent;

  import org.springframework.context.event.EventListener;

  import org.springframework.stereotype.Component;

  /**

  * @ClassName WriteBlogListenerAnnotation

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/10 16:30

  */

  @Component

  public class WriteBlogListenerAnnotation {

  @EventListener

  public void annotationListen(WriteBlogEvent writeBlogEvent) {

  String name = writeBlogEvent.getName();

  String address = writeBlogEvent.getAddress();

  System.out.println("基于注解:" + name + "在" + address + "写了一篇博客");

  }

  }

  package com.evan.spring.test;

  import com.evan.spring.config.AppConfig;

  import com.evan.spring.event.WriteBlogEvent;

  import org.springframework.context.annotation.AnnotationConfigApplicationContext;

  /**

  * @ClassName EventTest

  * @Description

  * @Author EvanWang

  * @Version 1.0.0

  * @Date 2019/12/10 15:56

  */

  public class EventTest {

  public static void main(String[] args) {

  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

  WriteBlogEvent writeBlogEvent = new WriteBlogEvent(ac, "Evan", "家里");

  ac.publishEvent(writeBlogEvent);

  }

  }

  运行结果:

  基于注解:Evan在家里写了一篇博客

  基于实现接口:Evan在家里写了一篇博客

  四、总结

  1、spring 如何得知有哪些监听器?

  通过2个步骤:1.从Bean工厂拿到所有ApplicationListener类型的Bean.

  2.扫描所有带@EventListener

  2、spring如何发布事件?

  大逻辑上通过2个步骤: 1.判断是否有监听器对该事件感兴趣

  2.调用监听器方法

感谢各位的阅读,以上就是"Spring事件发布与监听怎么实现"的内容了,经过本文的学习后,相信大家对Spring事件发布与监听怎么实现这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0