知識庫 / Spring / Spring MVC RSS 訂閱

Spring MVC 流式和 SSE 請求處理

Spring MVC
HongKong
10
01:44 PM · Dec 06 ,2025

1. 簡介

本教程演示了在 Spring MVC 5.x.x 中使用多個異步和流式對象的方法。

具體來説,我們將回顧三個關鍵類:

  • ResponseBodyEmitter
  • SseEmitter
  • StreamingResponseBody

此外,我們還將討論如何使用 JavaScript 客户端與之交互。

2. ResponseBodyEmitter

ResponseBodyEmitter 用於處理異步響應。

它還代表了多個子類的父類之一,其中我們將更詳細地進行分析。

2.1. 服務器端

最好使用 ResponseBodyEmitter 及其專用的異步線程,並將其包裹在 ResponseEntity 中(我們可以直接注入 emitter )。

@Controller
public class ResponseBodyEmitterController {
 
    private ExecutorService executor 
      = Executors.newCachedThreadPool();

    @GetMapping("/rbe")
    public ResponseEntity<ResponseBodyEmitter> handleRbe() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        executor.execute(() -> {
            try {
                emitter.send(
                  "/rbe" + " @ " + new Date(), MediaType.TEXT_PLAIN);
                emitter.complete();
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });
        return new ResponseEntity(emitter, HttpStatus.OK);
    }
}

因此,在上面的示例中,我們可以避免使用 CompleteableFutures,更復雜的異步承諾,或者使用 @Async註解。

相反,我們只需聲明異步實體並將其包裝在 ExecutorService提供的新的 Thread中。

2.2. 客户端

為了客户端使用,我們可以使用簡單的 XHR 方法並像普通 AJAX 操作一樣調用我們的 API 端點:

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
       //...
    });
};

xhr('http://localhost:8080/javamvcasync/rbe')
  .then(function(success){ //... });

3. SseEmitter

SseEmitter 實際上是 ResponseBodyEmitter 的子類,並提供內置的 服務器發送事件 (SSE) 支持。

3.1. 服務器端

下面我們來看一個利用該強大實體的示例控制器:

@Controller
public class SseEmitterController {
    private ExecutorService nonBlockingService = Executors
      .newCachedThreadPool();
    
    @GetMapping("/sse")
    public SseEmitter handleSse() {
         SseEmitter emitter = new SseEmitter();
         nonBlockingService.execute(() -> {
             try {
                 emitter.send("/sse" + " @ " + new Date());
                 // we could send more events
                 emitter.complete();
             } catch (Exception ex) {
                 emitter.completeWithError(ex);
             }
         });
         return emitter;
    }   
}

情況相當典型,但與我們常規的 REST 控制器相比,這裏有一些差異:

  • 首先,我們返回一個 
  • 此外,我們將核心響應信息包裝在一個 
  • 最後,我們使用 

3.2. 客户端

我們的客户端這次採用了稍微不同的方式,因為我們可以利用持續連接的Server-Sent Event庫:

var sse = new EventSource('http://localhost:8080/javamvcasync/sse');
sse.onmessage = function (evt) {
    var el = document.getElementById('sse');
    el.appendChild(document.createTextNode(evt.data));
    el.appendChild(document.createElement('br'));
};

4. StreamingResponseBody

最後,我們可以使用StreamingResponseBody直接將數據寫入OutputStream,然後再使用ResponseEntity將寫入的信息返回給客户端。

4.1. 服務器端

@Controller
public class StreamingResponseBodyController {
 
    @GetMapping("/srb")
    public ResponseEntity<StreamingResponseBody> handleRbe() {
        StreamingResponseBody stream = out -> {
            String msg = "/srb" + " @ " + new Date();
            out.write(msg.getBytes());
        };
        return new ResponseEntity(stream, HttpStatus.OK);
    }
}

4.2. 客户端

正如之前一樣,我們將使用常規的 XHR 方法來訪問上述控制器:

var xhr = function(url) {
    return new Promise(function(resolve, reject) {
        var xmhr = new XMLHttpRequest();
        //...
        xmhr.open("GET", url, true);
        xmhr.send();
        //...
    });
};

xhr('http://localhost:8080/javamvcasync/srb')
  .then(function(success){ //... });

接下來,讓我們來查看一下這些示例的成功應用。

5. 整合所有內容

在成功編譯我們的服務器並運行上述客户端(訪問提供的 index.jsp)後,我們應該在瀏覽器中看到以下內容:


並且在終端中看到以下內容:

&nbsp;

我們還可以直接調用端點並觀察響應在瀏覽器中流式傳輸。

6. 結論

儘管 FutureCompleteableFuture 在 Java 和 Spring 中已經證明了其強大的功能,但現在我們擁有了更多資源,可以更有效地處理高度併發的 Web 應用程序中的異步和流式數據。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.