Below is a step-by-step guide to creating a complete Spring Boot project using SSE (Server-Sent Events) to stream real-time updates (like stock prices). This example includes:
- Project setup
- Backend code with Spring Boot
- SSE streaming endpoint
- Frontend (HTML + JS) for consuming the stream
- Optional enhancements
ā 1. Project Setup
š ļø Using Spring Initializr
Go to https://start.spring.io and generate a project with the following settings:
- Project: Maven
- Language: Java
- Spring Boot: 3.x or higher
-
Dependencies:
- Spring Web
- Spring Boot DevTools (optional, for hot reload)
Click "Generate" to download the zip and extract it.
š 2. Directory Structure
sse-streaming-stock/
āāā src/
ā āāā main/
ā ā āāā java/
ā ā ā āāā com/example/sse/
ā ā ā āāā SseStreamingStockApplication.java
ā ā ā āāā controller/StockPriceController.java
ā ā āāā resources/
ā ā āāā static/
ā ā ā āāā index.html
ā ā āāā application.properties
āāā pom.xml
š¦ 3. Backend Code
š¹ SseStreamingStockApplication.java
package com.example.sse;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SseStreamingStockApplication {
public static void main(String[] args) {
SpringApplication.run(SseStreamingStockApplication.class, args);
}
}
š¹ controller/StockPriceController.java
package com.example.sse.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.time.LocalTime;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RestController
public class StockPriceController {
private final Random random = new Random();
@GetMapping("/stream-stocks")
public SseEmitter streamStockPrices() {
SseEmitter emitter = new SseEmitter(0L); // never timeout
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
int id = 0;
while (true) {
double price = 150 + random.nextDouble() * 10;
String symbol = "AAPL";
String time = LocalTime.now().toString();
String json = String.format(
"{\"symbol\": \"%s\", \"price\": %.2f, \"time\": \"%s\"}",
symbol, price, time
);
emitter.send(SseEmitter.event()
.id(String.valueOf(id++))
.name("stock-update")
.data(json));
Thread.sleep(1000);
}
} catch (IOException | InterruptedException e) {
emitter.completeWithError(e);
}
});
return emitter;
}
}
š 4. Frontend Code (Client)
š¹ src/main/resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stock Price Stream</title>
<style>
body { font-family: Arial; padding: 20px; }
.stock { font-size: 20px; color: green; }
</style>
</head>
<body>
<h1>Live Stock Price (SSE)</h1>
<div id="stock" class="stock">Waiting for updates...</div>
<script>
const eventSource = new EventSource('/stream-stocks');
eventSource.addEventListener("stock-update", (event) => {
const data = JSON.parse(event.data);
document.getElementById("stock").innerText =
`Symbol: ${data.symbol} | Price: $${data.price.toFixed(2)} | Time: ${data.time}`;
});
eventSource.onerror = () => {
document.getElementById("stock").innerText = "Connection lost.";
};
</script>
</body>
</html>
āļø 5. application.properties
# Prevent request timeout for long-lived SSE connections
server.servlet.context-path=/
spring.mvc.async.request-timeout=0
ā¶ļø 6. Run the Project
From the terminal, run:
./mvnw spring-boot:run
Then open http://localhost:8080 in your browser. You should see real-time stock price updates every second.
š§ Optional Enhancements
- Stream multiple stock symbols
- Add endpoint to subscribe to specific symbols
- Integrate real stock market API (e.g., Alpha Vantage, IEX Cloud)
- Use
Flux+WebFluxfor reactive SSE (instead of MVC) - Broadcast to multiple clients with
SseEmitterslist - Handle reconnections and missed events
Would you like a version using Spring WebFlux or a version with real stock data next?
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.