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

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

让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:国际域名空间、虚拟主机、营销软件、网站建设、汾阳网站维护、网站推广。

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

1、流程分析

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

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

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

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

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

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

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

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

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

2、流程分析

结构分析:

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

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

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

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

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

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

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

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

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

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

注:想直接了解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事件发布与监听怎么实现这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!


分享文章:Spring事件发布与监听怎么实现
文章路径:http://scyanting.com/article/gocdpi.html