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再寫一個事件釋出與監聽的例子。 鄭州人流醫院
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.呼叫監聽器方法
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2668233/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring事件釋出與監聽機制Spring事件
- SpringBoot系列——事件釋出與監聽Spring Boot事件
- Spring Boot 事件和監聽Spring Boot事件
- Spring事件監聽機制原始碼解析Spring事件原始碼
- Spring 事件監聽機制及原理分析Spring事件
- spring boot學習(3): SpringApplication 事件監聽Spring BootAPP事件
- Spring筆記(7) - Spring的事件和監聽機制Spring筆記事件
- spring-event-事件監聽機制實現Spring事件
- Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)Spring Boot事件
- 事件和事件監聽器事件
- JavaScript 事件監聽JavaScript事件
- springboot事件監聽Spring Boot事件
- js 監聽事件JS事件
- Flutter事件監聽Flutter事件
- 監聽滑鼠事件事件
- jQuery事件監聽jQuery事件
- SpringBoot事件監聽機制及觀察者模式/釋出訂閱模式Spring Boot事件模式
- 監聽鍵盤事件事件
- 初識事件監聽事件
- vue之監聽事件Vue事件
- ApplicationEventPublisher-Spring事件釋出器APPSpring事件
- 【Redis系列】Spring boot實現監聽Redis key失效事件RedisSpring Boot事件
- Nacos - 事件的註冊、取消與監聽(EventDispatcher)事件
- Spring5原始碼解析-Spring框架中的事件和監聽器Spring原始碼框架事件
- 如何移除事件監聽器事件
- deleted事件監聽報錯delete事件
- 探析 Spring 容器內部事件釋出Spring事件
- java springboot監聽事件和處理事件JavaSpring Boot事件
- 監聽所有模型的 saved 事件模型事件
- 如何監聽SAP CRM BOR事件事件
- flutter 中監聽滑動事件Flutter事件
- java 監聽 redis 過期事件JavaRedis事件
- h5 storage事件監聽H5事件
- Android監聽軟鍵盤收起與彈出Android
- VUE-UNI事件轉發監聽Vue事件
- SpringBoot事件監聽器原始碼分析Spring Boot事件原始碼
- 模型deleted事件監聽報錯解析模型delete事件
- Apache ZooKeeper - 事件監聽機制初探Apache事件