1. 程式人生 > 實用技巧 >《Java Spring框架》SpringBoot 自定義監聽器(含模擬實現監聽管理器)

《Java Spring框架》SpringBoot 自定義監聽器(含模擬實現監聽管理器)

前言

在實際工作的過程,我們經常需要監聽一個任務實際完成的情況和進度。所以引入監聽器的概念。

案例

下面程式碼都是在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)結束觸發該事件。