Spring Boot Basic – Tag 8 von 10
Von Elyndra Valen, Senior Entwicklerin bei Java Fleet Systems Consulting


WebSockets in Spring Boot

πŸ“ Deine Position im Kurs

TagThemaStatus
βœ… 1Erste REST APIAbgeschlossen
βœ… 2Spring Container & DIAbgeschlossen
βœ… 3@Controller & Thymeleaf BasicsAbgeschlossen
βœ… 4Thymeleaf Forms & MVC-PatternAbgeschlossen
βœ… 5Konfiguration & LoggingAbgeschlossen
βœ… 6DI & AOP im DetailAbgeschlossen
βœ… 7Scopes in SpringAbgeschlossen
β†’ 8WebSockets in Spring BootπŸ‘‰ DU BIST HIER!
9JAX-RS in Spring BootNoch nicht freigeschaltet
10Integration & AbschlussNoch nicht freigeschaltet

Modul: Spring Boot Basic (10 Arbeitstage)
Dein Ziel: Real-Time Communication mit WebSockets meistern!


πŸ“‹ Voraussetzungen

Du brauchst:

  • βœ… Tag 1-7 abgeschlossen
  • βœ… REST APIs verstehen (GET, POST)
  • βœ… @Controller und Model funktionieren
  • βœ… Thymeleaf Templates erstellen kΓΆnnen
  • βœ… Scopes verstehen (Session, Request)
  • πŸ’‘ Bonus: JavaScript Grundkenntnisse helfen!

⚑ Was dich heute erwartet

Bisher: Du hast REST APIs gebaut – aber jeder Request braucht eine Antwort.

Das Problem:

Client fragt: "Gibt es neue Nachrichten?"
Server antwortet: "Nein."

30 Sekunden spΓ€ter...
Client fragt: "Gibt es neue Nachrichten?"
Server antwortet: "Nein."

30 Sekunden spΓ€ter...
Client fragt: "Gibt es neue Nachrichten?"
Server antwortet: "Ja, hier ist eine!"

β†’ Client muss STΓ„NDIG fragen (Polling) 😫
β†’ Verschwendet Bandbreite und Ressourcen!

Die LΓΆsung: WebSockets!

Client verbindet sich mit Server
Server kann JEDERZEIT Nachrichten schicken!

Neue Nachricht kommt an
β†’ Server schickt SOFORT an Client!
β†’ Keine stΓ€ndigen Requests nΓΆtig! βœ…

🎯 Dein Lernpfad heute

Du arbeitest heute in mehreren aufbauenden Schwierigkeitsstufen. Hier hast du die Anwendung die hier verwendet wird, direkt von meinem GitHubi. Arbeite in deinem eigenen Tempo durch die Schritte:

🟒 Grundlagen (Schritte 1-3)

Was du lernst:

  • WebSockets verstehen – was ist das?
  • Unterschied HTTP vs WebSocket
  • STOMP Protocol kennenlernen
  • WebSocket Configuration in Spring Boot
  • Dependencies einrichten
  • Zwei AnsΓ€tze: Low-Level vs STOMP

Ziel: Du verstehst WebSockets und hast die Basis konfiguriert


🟑 Professional (Schritte 4-6)

Was du lernst:

  • @MessageMapping – der WebSocket-Controller
  • Chat-Application mit STOMP bauen
  • Broadcasting (an alle senden)
  • Live Person-Notifications implementieren
  • Frontend mit JavaScript/SockJS

Ziel: Production-Ready Live-Chat und Real-Time Updates

Dauer: ~3.5 Stunden


πŸ”΅ Bonus: Enterprise Features (Schritte 7-8)

Was du lernst:

  • Point-to-Point Messages (an einen User)
  • WebSocket Security implementieren
  • Reconnect-Logik & Heartbeat
  • Rate Limiting fΓΌr Messages
  • Presence Detection (wer ist online?)

Ziel: Enterprise-Level Real-Time Features beherrschen

Dauer: ~2 Stunden


πŸ’» Los geht’s!

🟒 GRUNDLAGEN (Schritte 1-3)

Schritt 1: WebSockets verstehen (45 Min)

1.1 Das Problem mit klassischem HTTP

Erinnerst du dich an REST APIs von Tag 1?

@RestController
@RequestMapping("/api/persons")
public class PersonApiController {
    
    @GetMapping
    public List<Person> getAllPersons() {
        return personService.getAllPersons();
    }
}

Was passiert:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   CLIENT (Browser)                      β”‚
β”‚                                         β”‚
β”‚   GET /api/persons                      β”‚
β”‚   ────────────────────────────►         β”‚
β”‚                                         β”‚
β”‚                        ◄────────────────│
β”‚                        [Person-Liste]   β”‚
β”‚                                         β”‚
β”‚   (30 Sekunden warten...)               β”‚
β”‚                                         β”‚
β”‚   GET /api/persons (erneut!)            β”‚
β”‚   ────────────────────────────►         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Das Problem:

  • ❌ Client muss aktiv fragen (Polling)
  • ❌ Server kann nicht von sich aus senden
  • ❌ Verschwendet Bandbreite (stΓ€ndige Requests)
  • ❌ Langsam (30 Sekunden bis neue Daten sichtbar)
  • ❌ Hohe Server-Last (viele unnΓΆtige Requests)

1.2 Die LΓΆsung: WebSockets

WebSocket = Dauerhafte Verbindung zwischen Client und Server!

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   CLIENT                    SERVER      β”‚
β”‚                                         β”‚
β”‚   WebSocket-Verbindung aufbauen         β”‚
β”‚   ════════════════════════►             β”‚
β”‚                                         β”‚
β”‚   ◄══════════════════════►              β”‚
β”‚   (Verbindung bleibt offen!)            β”‚
β”‚                                         β”‚
β”‚   Client sendet Nachricht               β”‚
β”‚   ────────────────────────►             β”‚
β”‚                                         β”‚
β”‚   Server sendet SOFORT zurΓΌck           β”‚
β”‚   ◄────────────────────────             β”‚
β”‚                                         β”‚
β”‚   Server sendet neue Daten (von sich!)  β”‚
β”‚   ◄────────────────────────             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Vorteile:

  • βœ… Bidirektional – beide Seiten senden
  • βœ… Real-Time – Daten sofort da
  • βœ… Effizient – eine Verbindung statt viele Requests
  • βœ… Server-Push – Server kann initiieren
  • βœ… Weniger Overhead – keine HTTP-Headers bei jedem Request

1.3 HTTP vs WebSocket – Der Vergleich

FeatureHTTP (REST)WebSocket
VerbindungPro Request neuDauerhaft offen
RichtungClient β†’ ServerBeide Richtungen
OverheadHoch (Headers)Niedrig (einmal Handshake)
Real-TimeNein (Polling nΓΆtig)Ja (sofort)
Use CasesCRUD, FormulareChat, Live-Updates, Gaming
Server-PushNeinJa

Wann was nutzen?

βœ… Nutze HTTP/REST fΓΌr:
- Formulare absenden
- Daten laden (einmalig)
- CRUD-Operationen
- Datei-Downloads
- Statische Inhalte

βœ… Nutze WebSockets fΓΌr:
- Chat-Applications
- Live-Notifications
- Multiplayer-Games
- Live-Dashboards
- Collaborative Editing
- Stock-Ticker

1.4 STOMP – Das Messaging-Protocol

WebSocket alleine = nur „Rohr“ fΓΌr Daten!

Du brauchst ein Protocol darΓΌber – wie strukturierst du Nachrichten?

STOMP = Simple Text Oriented Messaging Protocol

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Application (dein Java-Code)          β”‚
β”‚   ─────────────────────────────────     β”‚
β”‚   STOMP (Messaging-Protocol)            β”‚
β”‚   ─────────────────────────────────     β”‚
β”‚   WebSocket (Transport-Layer)           β”‚
β”‚   ─────────────────────────────────     β”‚
β”‚   TCP/IP                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Was macht STOMP:

  • πŸ“¨ Destinations – „/topic/chat“ (wie REST-Pfade)
  • 🎯 Subscribe – Client abonniert Topics
  • πŸ“€ Send – Client sendet an Destination
  • πŸ“₯ Receive – Server sendet an Subscriber
  • πŸ’¬ Frames – Strukturierte Nachrichten

Spring Boot nutzt STOMP standardmÀßig! Du musst es nicht manuell implementieren.


Schritt 2: Dependencies hinzufΓΌgen (15 Min)

pom.xml:

<dependencies>
    <!-- Existing dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    
    <!-- WebSocket Support -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
    <!-- Webjars fΓΌr Frontend-Libraries -->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>sockjs-client</artifactId>
        <version>1.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>stomp-websocket</artifactId>
        <version>2.3.4</version>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>5.3.0</version>
    </dependency>
</dependencies>

Maven reload nicht vergessen!

mvn clean install

Schritt 3: WebSocket Configuration (1.5 Stunden)

3.1 STOMP-basierte Configuration

config/WebSocketConfig.java:

package com.example.springbootbasic.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker  // WebSocket aktivieren!
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // Simple Broker fΓΌr Broadcasting
        config.enableSimpleBroker("/topic", "/queue");
        
        // Prefix fΓΌr Messages die an Server gehen
        config.setApplicationDestinationPrefixes("/app");
    }
    
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // WebSocket-Endpoint registrieren
        registry.addEndpoint("/ws")
                .withSockJS();  // Fallback wenn WebSocket nicht verfΓΌgbar
    }
}

Was bedeutet das?

/topic/* 
β†’ Broadcasting-Destinations (alle Subscriber bekommen Nachricht)
β†’ Beispiel: /topic/notifications (alle User sehen)

/queue/*
β†’ Point-to-Point-Destinations (nur ein User bekommt Nachricht)
β†’ Beispiel: /queue/user123/messages (nur User 123 sieht)

/app/*
β†’ Messages die an Server-Controller gehen
β†’ Beispiel: /app/chat.send β†’ @MessageMapping("/chat.send")

/ws
β†’ WebSocket-Endpoint (Client verbindet sich hierhin)
β†’ Mit SockJS-Fallback wenn Browser kein WebSocket kann

3.2 Message-Models erstellen

dto/ChatMessage.java:

package com.example.springbootbasic.dto;

import java.time.LocalDateTime;

public class ChatMessage {
    
    private String content;
    private String sender;
    private MessageType type;
    private LocalDateTime timestamp;
    
    public enum MessageType {
        CHAT,
        JOIN,
        LEAVE
    }
    
    // Constructors
    public ChatMessage() {
        this.timestamp = LocalDateTime.now();
    }
    
    public ChatMessage(String content, String sender, MessageType type) {
        this.content = content;
        this.sender = sender;
        this.type = type;
        this.timestamp = LocalDateTime.now();
    }
    
    // Getters and Setters
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    
    public String getSender() { return sender; }
    public void setSender(String sender) { this.sender = sender; }
    
    public MessageType getType() { return type; }
    public void setType(MessageType type) { this.type = type; }
    
    public LocalDateTime getTimestamp() { return timestamp; }
    public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}

βœ… Checkpoint Grundlagen

Kontrolliere:

  • [ ] Du verstehst was WebSockets sind
  • [ ] Du kennst den Unterschied zu REST APIs
  • [ ] Du weißt was STOMP ist
  • [ ] Dependencies sind in pom.xml
  • [ ] WebSocketConfig ist erstellt
  • [ ] Du verstehst /topic vs /queue
  • [ ] ChatMessage DTO ist erstellt

Alles βœ…? Weiter zu 🟑 Professional!


🟑 PROFESSIONAL (Schritte 4-6)

Schritt 4: WebSocket-Controller erstellen (1.5 Stunden)

4.1 Chat-Controller mit @MessageMapping

controller/ChatController.java:

package com.example.springbootbasic.controller;

import com.example.springbootbasic.dto.ChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {
    
    private static final Logger log = LoggerFactory.getLogger(ChatController.class);
    
    @MessageMapping("/chat.send")  // Client sendet an /app/chat.send
    @SendTo("/topic/public")       // Broadcast an alle die /topic/public abonniert haben
    public ChatMessage sendMessage(@Payload ChatMessage chatMessage) {
        log.info("Received message from {}: {}", 
            chatMessage.getSender(), 
            chatMessage.getContent());
        
        return chatMessage;  // Wird an alle Subscriber gesendet
    }
    
    @MessageMapping("/chat.join")
    @SendTo("/topic/public")
    public ChatMessage addUser(@Payload ChatMessage chatMessage,
                                SimpMessageHeaderAccessor headerAccessor) {
        
        // Username in WebSocket-Session speichern
        headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
        
        log.info("User joined: {}", chatMessage.getSender());
        
        chatMessage.setType(ChatMessage.MessageType.JOIN);
        chatMessage.setContent(chatMessage.getSender() + " ist dem Chat beigetreten!");
        
        return chatMessage;
    }
}

Was passiert hier?

Client sendet:
POST /app/chat.send
{
  "content": "Hallo Welt!",
  "sender": "Max",
  "type": "CHAT"
}

Server empfΓ€ngt:
@MessageMapping("/chat.send") wird aufgerufen
β†’ Verarbeitet Nachricht
β†’ return chatMessage

Server sendet:
An /topic/public (Broadcasting!)
β†’ ALLE die /topic/public abonniert haben
β†’ bekommen die ChatMessage!

4.2 WebSocket Event Listener

listener/WebSocketEventListener.java:

package com.example.springbootbasic.listener;

import com.example.springbootbasic.dto.ChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Component
public class WebSocketEventListener {
    
    private static final Logger log = LoggerFactory.getLogger(WebSocketEventListener.class);
    
    private final SimpMessageSendingOperations messagingTemplate;
    
    public WebSocketEventListener(SimpMessageSendingOperations messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }
    
    @EventListener
    public void handleWebSocketConnectListener(SessionConnectedEvent event) {
        log.info("New WebSocket connection established");
    }
    
    @EventListener
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        
        String username = (String) headerAccessor.getSessionAttributes().get("username");
        
        if (username != null) {
            log.info("User disconnected: {}", username);
            
            ChatMessage chatMessage = new ChatMessage();
            chatMessage.setType(ChatMessage.MessageType.LEAVE);
            chatMessage.setSender(username);
            chatMessage.setContent(username + " hat den Chat verlassen!");
            
            messagingTemplate.convertAndSend("/topic/public", chatMessage);
        }
    }
}

Schritt 5: Frontend mit WebSocket (2 Stunden)

5.1 Chat-Template erstellen

templates/chat.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Spring Boot Chat</title>
    <link rel="stylesheet" th:href="@{/webjars/bootstrap/5.3.0/css/bootstrap.min.css}">
    
    <style>
        body {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        #chat-page { display: none; }
        
        .chat-container {
            max-width: 700px;
            background: white;
            border-radius: 10px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.2);
        }
        
        .chat-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            border-radius: 10px 10px 0 0;
        }
        
        .chat-messages {
            height: 400px;
            overflow-y: auto;
            padding: 20px;
            background: #f8f9fa;
        }
        
        .message {
            margin-bottom: 15px;
            padding: 10px 15px;
            border-radius: 20px;
            max-width: 70%;
        }
        
        .message.own {
            background: #667eea;
            color: white;
            margin-left: auto;
            text-align: right;
        }
        
        .message.other {
            background: white;
            border: 1px solid #dee2e6;
        }
        
        .message.event {
            background: #fff3cd;
            text-align: center;
            margin: 10px auto;
            font-style: italic;
            max-width: 100%;
        }
    </style>
</head>
<body>

<!-- Username Page -->
<div id="username-page">
    <div class="username-form" style="background: white; padding: 40px; border-radius: 10px;">
        <h2 class="mb-4">πŸš€ Spring Boot Chat</h2>
        <form id="usernameForm">
            <input type="text" id="username" class="form-control mb-3" placeholder="Dein Name" required>
            <button type="submit" class="btn btn-primary w-100">Chat beitreten</button>
        </form>
    </div>
</div>

<!-- Chat Page -->
<div id="chat-page" class="chat-container">
    <div class="chat-header">
        <h4>πŸ’¬ Spring Boot Live Chat</h4>
        <small id="connected-user"></small>
    </div>
    
    <div id="chat-messages" class="chat-messages"></div>
    
    <div class="p-3">
        <form id="messageForm">
            <div class="input-group">
                <input type="text" id="message" class="form-control" placeholder="Nachricht...">
                <button type="submit" class="btn btn-primary">Senden</button>
            </div>
        </form>
    </div>
</div>

<script th:src="@{/webjars/sockjs-client/1.5.1/sockjs.min.js}"></script>
<script th:src="@{/webjars/stomp-websocket/2.3.4/stomp.min.js}"></script>

<script>
    let stompClient = null;
    let currentUser = null;
    
    const usernamePage = document.querySelector('#username-page');
    const chatPage = document.querySelector('#chat-page');
    const usernameForm = document.querySelector('#usernameForm');
    const messageForm = document.querySelector('#messageForm');
    const messageInput = document.querySelector('#message');
    const chatMessages = document.querySelector('#chat-messages');
    
    usernameForm.addEventListener('submit', function(e) {
        e.preventDefault();
        currentUser = document.querySelector('#username').value.trim();
        
        if (currentUser) {
            usernamePage.style.display = 'none';
            chatPage.style.display = 'block';
            connect();
        }
    });
    
    function connect() {
        const socket = new SockJS('/ws');
        stompClient = Stomp.over(socket);
        
        stompClient.connect({}, onConnected, onError);
    }
    
    function onConnected() {
        stompClient.subscribe('/topic/public', onMessageReceived);
        
        stompClient.send("/app/chat.join", {},
            JSON.stringify({sender: currentUser, type: 'JOIN'})
        );
    }
    
    function onError(error) {
        console.error('WebSocket Error:', error);
    }
    
    messageForm.addEventListener('submit', function(e) {
        e.preventDefault();
        const content = messageInput.value.trim();
        
        if (content && stompClient) {
            stompClient.send("/app/chat.send", {},
                JSON.stringify({sender: currentUser, content: content, type: 'CHAT'})
            );
            messageInput.value = '';
        }
    });
    
    function onMessageReceived(payload) {
        const message = JSON.parse(payload.body);
        const messageElement = document.createElement('div');
        
        if (message.type === 'JOIN' || message.type === 'LEAVE') {
            messageElement.classList.add('message', 'event');
            messageElement.textContent = message.content;
        } else {
            messageElement.classList.add('message');
            messageElement.classList.add(message.sender === currentUser ? 'own' : 'other');
            messageElement.textContent = message.sender + ': ' + message.content;
        }
        
        chatMessages.appendChild(messageElement);
        chatMessages.scrollTop = chatMessages.scrollHeight;
    }
</script>

</body>
</html>

5.2 Controller fΓΌr View

@Controller
public class ChatViewController {
    
    @GetMapping("/chat")
    public String chatPage() {
        return "chat";
    }
}

5.3 Testen!

# App starten
mvn spring-boot:run

# Browser 1: http://localhost:8080/chat
Username: Alice
Nachricht: "Hallo Bob!"

# Browser 2: http://localhost:8080/chat
Username: Bob
β†’ Sieht: "Alice ist dem Chat beigetreten!"
β†’ Sieht: "Alice: Hallo Bob!"

Real-Time Chat funktioniert! βœ…


Schritt 6: Person-Notifications (1.5 Stunden)

6.1 PersonService erweitern

@Service
public class PersonService {
    
    private final SimpMessagingTemplate messagingTemplate;
    
    public PersonService(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }
    
    public Person createPerson(Person person) {
        person.setId(idCounter.getAndIncrement());
        persons.add(person);
        
        // WebSocket Notification senden!
        PersonNotification notification = new PersonNotification(
            "Neue Person: " + person.getFirstname(),
            person,
            NotificationType.PERSON_CREATED
        );
        
        messagingTemplate.convertAndSend("/topic/persons", notification);
        
        return person;
    }
}

ALLE Browser die /topic/persons abonniert haben bekommen sofort die Notification! βœ…


βœ… Checkpoint Professional

Kontrolliere:

  • [ ] ChatController mit @MessageMapping funktioniert
  • [ ] WebSocketEventListener implementiert
  • [ ] Chat-Frontend mit JavaScript erstellt
  • [ ] SockJS und STOMP.js eingebunden
  • [ ] Live-Chat zwischen zwei Browsern funktioniert
  • [ ] PersonService sendet Notifications
  • [ ] Du verstehst Broadcasting

Alles βœ…? Weiter zu πŸ”΅ Enterprise Features!


πŸ”΅ BONUS: ENTERPRISE FEATURES (Schritte 7-8)

Schritt 7: Point-to-Point Messages (1 Stunde)

7.1 Private Nachrichten

@Controller
public class PrivateMessageController {
    
    private final SimpMessagingTemplate messagingTemplate;
    
    public PrivateMessageController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }
    
    @MessageMapping("/chat.private")
    public void sendPrivateMessage(@Payload ChatMessage message) {
        // Nachricht an spezifischen User senden
        messagingTemplate.convertAndSendToUser(
            message.getRecipient(),  // Username
            "/queue/private",        // Destination
            message                  // Payload
        );
    }
}

Was passiert:

  • User „Alice“ sendet private Message an „Bob“
  • NUR Bob empfΓ€ngt die Nachricht
  • Andere User sehen nichts

Schritt 8: WebSocket Security & Best Practices (1 Stunde)

8.1 Security Configuration

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
    
    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
            .simpDestMatchers("/app/**").authenticated()
            .simpSubscribeDestMatchers("/topic/**", "/queue/**").authenticated()
            .anyMessage().denyAll();
    }
}

8.2 Reconnect-Logik

let reconnectAttempts = 0;
const maxAttempts = 5;

function connect() {
    const socket = new SockJS('/ws');
    stompClient = Stomp.over(socket);
    
    stompClient.connect({}, onConnected, function(error) {
        if (reconnectAttempts < maxAttempts) {
            reconnectAttempts++;
            const delay = Math.min(1000 * Math.pow(2, reconnectAttempts - 1), 30000);
            setTimeout(() => connect(), delay);
        }
    });
}

βœ… Checkpoint Enterprise Features

Kontrolliere:

  • [ ] Point-to-Point Messages funktionieren
  • [ ] WebSocket Security konfiguriert
  • [ ] Reconnect-Logik implementiert
  • [ ] Du verstehst /topic vs /queue
  • [ ] Du kannst Production-Ready WebSockets bauen

Alles βœ…? Du bist jetzt ein WebSocket-Profi! πŸŽ‰

Hier sind noch einige empfehlenswerte externe Links zum Thema β€žWebSockets mit Spring Boot / Spring Framework”, die du gut zur Vertiefung verwenden kannst!

  • Offizielle Spring-Guide β€žUsing WebSocket to build an interactive web applicationβ€œ – zeigt Schritt fΓΌr Schritt, wie man mit STOMP ΓΌber WebSocket in Spring eine interaktive Anwendung realisiert. Home
  • Offizielle Dokumentation β€žWebSockets :: Spring Frameworkβ€œ – tiefgehende Beschreibung der WebSocket-UnterstΓΌtzung im Spring Framework inklusive SOCKJS, STOMP und Protokollwechsel. Home
  • Baeldung-Tutorial β€žIntro to WebSockets with Springβ€œ – eines der gut strukturierten Tutorials mit Praxisbeispielen und dem typischen Chat-Use-Case. Baeldung on Kotlin
  • Spring Boot eigene Dokumentation β€žWebSockets :: Spring Bootβ€œ – zeigt, wie Spring Boot WebSockets automatisch unterstΓΌtzt und wie die Konfiguration aussieht. Home
  • Blogbeitrag β€žWebSocket + Spring Boot: Build a Real-time, Bidirectional Applicationsβ€œ – verbindet praktische Umsetzung mit Performance-Hinweisen und Real-Time-Szenarien. Medium

πŸ”₯ Elyndras Real Talk:

Nova kam heute zu mir, vΓΆllig begeistert: „Elyndra! Ich hab gerade verstanden warum WebSockets so cool sind! Ich muss nicht mehr alle 5 Sekunden den Server fragen ob es was Neues gibt!“

Ich lΓ€chelte: „Genau. Das ist der Unterschied zwischen ‚Are we there yet?‘ alle 30 Sekunden fragen und dass der Server einfach sagt ‚We’re here!‘ wenn es soweit ist.“

Die AutoTech Real-Time Dashboard Story

Das war 2020, wΓ€hrend meines dritten Jahres bei AutoTech. Wir hatten ein Production-Monitoring Dashboard fΓΌr unsere Autofabrik.

Das Problem: Polling-HΓΆlle

50 gleichzeitig offene Dashboards
Γ— 1 Request alle 5 Sekunden
Γ— 60 Sekunden Γ— 60 Minuten
= 1,080,000 Requests pro Stunde
= NUR fΓΌrs Dashboard! πŸ’₯

Die Datenbank war am Limit.

Code Sentinel kam zu mir: „Wir brauchen Real-Time Updates OHNE Polling. Kannst du WebSockets implementieren?“

Die WebSocket-LΓΆsung:

1 Database-Query pro Sekunde
Γ— 1 WebSocket-Broadcast
= 3,600 Queries pro Stunde
(statt 1,080,000!)

CPU-Last: -95%
Database-Load: -99%
Update-Latency: 1 Sekunde (statt 5)
βœ… Production-Ready!

Nova war beeindruckt: „Das ist ja 300x weniger Last!“

„Exakt,“ sagte ich. „Das ist der Unterschied zwischen ‚Fragen‘ und ‚Informiert werden‘. WebSockets sind wie Push-Notifications fΓΌr den Browser.“


πŸ†˜ Troubleshooting

Problem 1: WebSocket-Verbindung schlΓ€gt fehl

LΓΆsung:

// βœ… Richtig - mit SockJS
const socket = new SockJS('/ws');

Problem 2: Nachrichten kommen nicht an

LΓΆsung:

// Subscribe nicht vergessen!
stompClient.subscribe('/topic/public', function(message) {
    console.log(message);
});

❓ FAQ

Q: Wann WebSockets, wann REST?
A: REST fΓΌr Request/Response (Formular absenden). WebSockets fΓΌr Real-Time (Chat, Live-Updates).

Q: Was ist STOMP?
A: Ein Messaging-Protocol ΓΌber WebSocket. Strukturiert Nachrichten mit Destinations.

Q: Was macht SockJS?
A: Fallback wenn Browser kein WebSocket unterstΓΌtzt.

Q: Unterschied /topic/ vs /queue/*?*
A: /topic/* = Broadcasting (alle), /queue/* = Point-to-Point (einer).


Spring Boot Basic - Tag 8

WebSockets

Frage 1 von 10

Was ist der Hauptunterschied zwischen HTTP und WebSocket?

Frage 2 von 10

Was ist STOMP?

Frage 3 von 10

Was macht @EnableWebSocketMessageBroker?

Frage 4 von 10

Was bedeutet /topic/* in STOMP?

Frage 5 von 10

Was bedeutet /queue/* in STOMP?

Frage 6 von 10

Was macht @MessageMapping("/chat.send")?

Frage 7 von 10

Was macht @SendTo("/topic/public")?

Frage 8 von 10

Welche JavaScript-Library nutzt man fΓΌr WebSocket-Client?

Frage 9 von 10

WofΓΌr steht .withSockJS() in Spring WebSocket Config?

Frage 10 von 10

WofΓΌr nutzt man WebSockets typischerweise?

πŸ“… NΓ€chster Kurstag: Tag 9

Morgen: JAX-RS in Spring Boot – REST mit Java Standards!


Du hast 80% geschafft! πŸ’ͺ


πŸ“₯ Download & Ressourcen

Projekt: SpringBootWebSockets-v1.0.zip

EnthΓ€lt:

  • Live-Chat Application
  • Person-Notifications
  • WebSocket Security
  • Alle Templates

Du kannst jetzt:

  • βœ… WebSockets verstehen
  • βœ… STOMP nutzen
  • βœ… Live-Chat bauen
  • βœ… Real-Time Updates
  • βœ… Broadcasting
  • βœ… Point-to-Point Messages
  • βœ… Production-Ready WebSockets! πŸš€

Keep coding, keep learning!


Tags: #SpringBoot #WebSockets #RealTime #STOMP #LiveChat #Tag8

Autor

  • Elyndra Valen

    28 Jahre alt, wurde kΓΌrzlich zur Senior Entwicklerin befΓΆrdert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen ZusammenhΓ€nge und Architektur-Entscheidungen zu verstehen. Sie ist die BrΓΌcke zwischen Junior- und Senior-Welt im Team.