Backend Development 10 min read

Using Spring's ResponseBodyEmitter for Real‑Time Streaming Responses

This article introduces Spring Framework's ResponseBodyEmitter, explains its advantages over SSE for asynchronous HTTP responses, demonstrates practical usage with a real‑time log‑streaming example, outlines core methods, working principles, best‑practice considerations, and compares it with traditional Streaming and Server‑Sent Events.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Using Spring's ResponseBodyEmitter for Real‑Time Streaming Responses

ResponseBodyEmitter Overview

With the rise of ChatGPT, streaming output technologies have become popular, and many developers started exploring Server‑Sent Events (SSE). A simpler way to achieve streaming output in Spring is using ResponseBodyEmitter , which has been part of Spring Framework since version 4.2.

What ResponseBodyEmitter Does

Compared with SSE, ResponseBodyEmitter is easier to use. It is an interface for asynchronous HTTP responses that allows data to be sent to the client incrementally instead of all at once, making it ideal for long‑running or streaming scenarios.

Typical Use Cases

Long Polling : Keep the connection open until data is available.

Server‑Sent Events (SSE) : Continuously push events from server to client.

Streaming Transfer : Gradually send large amounts of data such as file downloads or live data streams.

Asynchronous Processing : Return partial results of time‑consuming tasks to improve user experience.

Business Scenario Examples

ResponseBodyEmitter can be used for real‑time progress bars, chat applications, stock price updates, system log streaming, and AI streaming responses.

Real‑Time Log Streaming Demo

The following example shows how to create a Spring Boot controller that streams logs in real time using ResponseBodyEmitter :

Create Controller

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;

@RestController
@RequestMapping("/api/log")
public class LogController {
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public ResponseBodyEmitter streamLogs() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        // Start async thread to generate and send data
        new Thread(() -> {
            try {
                while (true) {
                    String logEntry = getLatestLogEntry();
                    if (logEntry != null) {
                        emitter.send(logEntry);
                    }
                    // Check for new logs every second
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                // End response with error information
                emitter.completeWithError(e);
            }
        }).start();
        return emitter;
    }

    private String getLatestLogEntry() {
        // Simulate fetching the latest log line
        return "2025-02-12 12:00:00 - INFO: User logged in successfully.";
    }
}

Running Result

When the application runs and the endpoint /api/log/stream is accessed, the client receives a continuously updated log stream, with a new entry pushed every second.

Core Methods of ResponseBodyEmitter

send(Object data) : Sends data to the client; can be called multiple times.

complete() : Ends the response stream when all data has been sent.

onTimeout(Runnable callback) : Sets a timeout callback.

onCompletion(Runnable callback) : Sets a completion callback.

How It Works

Asynchronous Data Generation & Push

Traditional HTTP waits for the whole response before sending. ResponseBodyEmitter breaks this pattern by allowing the server to generate and push data asynchronously during task execution.

Chunked Transfer Mechanism

It uses HTTP chunked encoding, so the server does not need to set Content‑Length in advance; each chunk carries its own length, enabling the client to process data as soon as it arrives.

Connection Lifecycle Management

After all data is sent, call complete() to close the connection. If an error occurs, call completeWithError() to terminate the response and propagate the error.

Precautions

Client Support : Most modern browsers support chunked transfer, but older clients may have compatibility issues.

Timeout Settings : Configure a timeout to avoid long‑living connections, e.g., emitter.onTimeout(() -> emitter.complete());

Thread Safety : The send() method is thread‑safe, but manage thread lifecycles to prevent resource leaks.

Connection Closure : Always invoke complete() or completeWithError() when processing finishes.

Comparison with Streaming and SSE

Streaming : Uses OutputStream directly; flexible but requires manual stream management.

Server‑Sent Events (SSE) : Based on text/event-stream protocol; suitable for event‑push scenarios but requires client support for SSE.

ResponseBodyEmitter : Works with any HTTP client, integrates smoothly with Spring, and provides a lightweight streaming solution.

Conclusion

ResponseBodyEmitter is a lightweight streaming solution provided by Spring that significantly improves user experience in high‑concurrency and real‑time scenarios. By leveraging its incremental send capability, developers can easily implement real‑time data push for progress bars, chat, stock updates, log streaming, and AI responses.

backendJavaStreamingSpringHTTPSSEResponseBodyEmitter
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.