前言
在实际工作的过程,我们经常需要监听一个任务实际完成的情况和进度。所以引入监听器的概念。
案例
下面代码都是在Spring Boot 框架下完成
设计一个任务:本任务简单设置:一个循环,每次循环都发布一下进度情况。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Component public class EventProcess { @Autowired private ApplicationContext applicationContext; public void process(){ for(int i = 1; i <= 10; i++){ try { /**** 为了演示效果睡眠一秒 ****/ Thread.sleep(1000L); applicationContext.publishEvent(new CustomEvent(this,i)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
设置一个外部需要关心内容的类
import org.springframework.context.ApplicationEvent; public class CustomEvent extends ApplicationEvent { private int index; /** * Create a new {@code ApplicationEvent}. * * @param source the object on which the event initially occurred or with * which the event is associated (never {@code null}) */ public CustomEvent(Object source,int index) { super(source); this.index = index; } public int getIndex() { return index; } }
再设置一个监听器:重写onApplicationEvent 方法,并且将事件处理成前端可以识别的内容。
import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class CustomApplicationListener implements ApplicationListener<CustomEvent> { private String msg; @Override public void onApplicationEvent(CustomEvent event) { msg = "第"+event.getIndex()+"轮"; System.out.println(msg); } public String getMsg() { return msg; } }
controller 设置请求
import com.example.event.CustomApplicationListener; import com.example.event.EventProcess; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController2 { @Autowired EventProcess eventProcess; @Autowired CustomApplicationListener customApplicationListener; @RequestMapping("/event.do") public String event(){ eventProcess.process(); return "success"; } @RequestMapping("/listener.do") public String listener(){ return customApplicationListener.getMsg(); } }
测试:
先请求event 让任务跑起来。
再请求listener 可以显示现在任务的进度 。
原理(手写监听管理器)
以下内容不需要任务框架,只需要JDK即可
public class AEvent extends ApplicationEvent { }
public class ApplicationEvent { }
public class BEvent extends ApplicationEvent { }
/** * 文件上传事件 */ 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 int getReadSize() { return readSize; } }
import demo.knowledgepoints.monitor.event.AEvent; public class AAListener implements ApplicationListener<AEvent> { @Override public void onEvent(AEvent aEvent) { System.out.println("AA也监听到了"); } }
import demo.knowledgepoints.monitor.event.AEvent; public class AListener implements ApplicationListener<AEvent>{ @Override public void onEvent(AEvent aEvent) { System.out.println("监听到了A事件"); } }
import demo.knowledgepoints.monitor.event.ApplicationEvent; /** * 这里定义泛型是为了后面统一方法监听的时候获取实现该接口上设置的具体事件 * @param <E> */ public interface ApplicationListener< E extends ApplicationEvent> { void onEvent(E e); }
import demo.knowledgepoints.monitor.event.BEvent; public class BListener implements ApplicationListener<BEvent> { @Override public void onEvent(BEvent bEvent) { System.out.println("监听到了B事件"); } }
import demo.knowledgepoints.monitor.event.FileUploadEvent; public class FileUploadListener implements ApplicationListener<FileUploadEvent> { @Override public void onEvent(FileUploadEvent event) { double i1 = event.getFileSize(); double d = event.getReadSize()/i1; /********* * 正常情况这边应该将值保存下来放入一个静态变量中, * 然后前端访问的时候将这个静态变量返回给前端。 *********/ System.out.println("当前文件上传进度百分比:"+d*100+"%"); } }
最重要的一步
import demo.knowledgepoints.monitor.event.ApplicationEvent; import demo.knowledgepoints.monitor.listener.ApplicationListener; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.List; /** * 事件管理器 */ public class ListenerManage { //保存所有的监听器 public static List<ApplicationListener<?>> list = new ArrayList<>(); /** * Spring Boot 是扫描项目添加事件的 * @param listener */ public static void addListener(ApplicationListener listener){ list.add(listener); } /** * 监听事件 * @param event */ public static void pushEvent(ApplicationEvent event){ for (ApplicationListener applicationListener : list) { /**********获取泛型的类型************/ Class tClass = (Class)((ParameterizedType)applicationListener.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0]; if (tClass.equals(event.getClass())) { applicationListener.onEvent(event); } } } }
文件上传监听测试:
import demo.knowledgepoints.monitor.event.FileUploadEvent; import demo.knowledgepoints.monitor.listener.FileUploadListener; import demo.knowledgepoints.monitor.manage.ListenerManage; import java.io.*; public class FileUtil { public static int READ_SIZE= 100; public static void fileWrite(InputStream is, OutputStream os) throws Exception{ BufferedInputStream bis = new BufferedInputStream(is); BufferedOutputStream bos = new BufferedOutputStream(os); //文件总大小 int fileSize = is.available(); //一共读取了多少 int readSize = 0; byte[] b = new byte[READ_SIZE]; boolean flag = true; while (flag){ //文件实在小于第一次读的时候 if (fileSize<READ_SIZE){ byte[] bytes = new byte[fileSize]; bis.read(bytes); bos.write(bytes); readSize = fileSize; flag = false; }else if(fileSize<readSize+READ_SIZE){ byte[] bytes = new byte[fileSize-readSize]; readSize = fileSize; bis.read(bytes); bos.write(bytes); flag = false; }else{ bis.read(b); readSize +=READ_SIZE; bos.write(b); } ListenerManage.pushEvent(new FileUploadEvent(fileSize,readSize)); } bis.close(); bos.close(); } public static void main(String[] args) throws Exception { ListenerManage.addListener(new FileUploadListener()); fileWrite(new FileInputStream(new File("src\demo\knowledgepoints\monitor\file\a.txt")),new FileOutputStream(new File("src\demo\knowledgepoints\monitor\file\b.txt"))); } }
运行结果:
监听其他事件
import demo.knowledgepoints.monitor.event.AEvent; import demo.knowledgepoints.monitor.event.BEvent; import demo.knowledgepoints.monitor.listener.AAListener; import demo.knowledgepoints.monitor.listener.AListener; import demo.knowledgepoints.monitor.listener.BListener; import demo.knowledgepoints.monitor.manage.ListenerManage; import java.util.Scanner; public class OtherListenerUtil { public static void main(String[] args) { ListenerManage.addListener(new AListener()); ListenerManage.addListener(new BListener()); ListenerManage.addListener(new AAListener()); Scanner scanner = new Scanner(System.in); while (true){ System.out.println("选择你要监听的对象(A,B):"); String scan = scanner.next(); if (scan.equals("A")){ ListenerManage.pushEvent(new AEvent()); }else if (scan.equals("B")){ ListenerManage.pushEvent(new BEvent()); } } } }
运行结果:
总结
监听器是监听任务是否正常运行,spring 本身也有内部不少事件需要被监听:
- 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext接口中的refresh()方法时被触发。
- 上下文开始事件(ContextStartedEvent):当容器ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
- 上下文停止事件(ContextStoppedEvent):当容ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
- 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
- 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。