Introducing "NAMO" Real-Time Speech AI Model: On-Device & Hybrid Cloud šŸ“¢PRESS RELEASE

Spring Boot WebSocket: Build Real-time Applications

A comprehensive guide to building real-time applications with Spring Boot WebSocket, covering basic setup, STOMP, SockJS, security, and a chat example.

Spring Boot WebSocket: Build Real-time Applications

Introduction to Spring Boot WebSocket

WebSockets provide a persistent connection between a client and a server, enabling full-duplex communication. This is a significant improvement over the traditional HTTP request-response model, especially for real-time applications. Spring Boot offers excellent support for WebSockets, making it easy to integrate real-time functionality into your applications. This article will guide you through various aspects of Spring Boot WebSocket, from basic setup to advanced messaging with STOMP and browser compatibility with SockJS. We'll explore different approaches, including simple WebSockets, STOMP, and SockJS, along with practical examples.

Understanding WebSockets and the Need for Real-time Communication

The WebSocket protocol facilitates full-duplex communication over a single TCP connection. This means that the client and server can both send data to each other at any time, without the need for repeated requests. In contrast, the HTTP request-response model requires the client to initiate each communication by sending a request, and the server responds to each request individually.
WebSockets are particularly advantageous for real-time applications such as chat applications, online games, live notifications, and collaborative editing tools. These applications require instant updates and low latency, which WebSockets provide efficiently. Traditional HTTP can be inefficient in these scenarios because of the overhead of establishing a new connection for each message. WebSockets maintain a persistent connection, reducing latency and improving the overall user experience.

Setting up a Spring Boot Project for WebSocket Integration

To get started with Spring Boot WebSocket, you'll need to create a new Spring Boot project. The easiest way to do this is by using the Spring Initializr (

https://start.spring.io/

).
  1. Visit the Spring Initializr website.
  2. Choose your project's metadata (e.g., Maven or Gradle, Java version).
  3. Add the spring-boot-starter-websocket dependency.
  4. Generate the project and download the ZIP file.
  5. Extract the ZIP file to your desired location.
The basic project structure will look like this:
1my-websocket-app/
2ā”œā”€ā”€ src/
3│   ā”œā”€ā”€ main/
4│   │   ā”œā”€ā”€ java/
5│   │   │   └── com/
6│   │   │       └── example/
7│   │   │           └── websocket/
8│   │   ā”œā”€ā”€ resources/
9│   │   │   ā”œā”€ā”€ application.properties
10│   │   │   └── static/
11│   │   └── webapp/
12│   └── test/
13ā”œā”€ā”€ pom.xml (or build.gradle)
14
Configuration files (like application.properties) are typically placed in the src/main/resources directory. Your Java source code will reside in the src/main/java directory.
1<!-- pom.xml dependencies -->
2<dependency>
3    <groupId>org.springframework.boot</groupId>
4    <artifactId>spring-boot-starter-websocket</artifactId>
5</dependency>
6

Configuring Basic Spring Boot WebSocket Support

To enable WebSocket support in your Spring Boot application, you'll use the @EnableWebSocket annotation. This annotation enables the processing of WebSocket requests. You also need to create a WebSocket handler class that implements the WebSocketConfigurer interface to register your handler and define the WebSocket endpoint.
1import org.springframework.context.annotation.Configuration;
2import org.springframework.web.socket.config.annotation.EnableWebSocket;
3import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
4import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
5
6@Configuration
7@EnableWebSocket
8public class WebSocketConfig implements WebSocketConfigurer {
9
10    @Override
11    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
12        registry.addHandler(new MyWebSocketHandler(), "/my-websocket-endpoint").setAllowedOrigins("*");
13    }
14}
15
1import org.springframework.web.socket.TextMessage;
2import org.springframework.web.socket.WebSocketSession;
3import org.springframework.web.socket.handler.TextWebSocketHandler;
4
5public class MyWebSocketHandler extends TextWebSocketHandler {
6
7    @Override
8    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
9        String receivedMessage = message.getPayload();
10        System.out.println("Received: " + receivedMessage);
11        TextMessage response = new TextMessage("Server received: " + receivedMessage);
12        session.sendMessage(response);
13    }
14}
15
In the WebSocketConfig class, the registerWebSocketHandlers method registers a MyWebSocketHandler at the /my-websocket-endpoint endpoint. The setAllowedOrigins("*") allows connections from any origin, but in production, you should restrict this to specific origins for security reasons.
The MyWebSocketHandler class extends TextWebSocketHandler and overrides the handleTextMessage method to process incoming text messages.

Implementing a Simple WebSocket Endpoint

The handleTextMessage method in your WebSocket handler is where you'll process incoming messages. This method receives a WebSocketSession object, which represents the connection with the client, and a TextMessage object, which contains the message data.
To send a message back to the client, you can use the session.sendMessage() method. You need to create a TextMessage object containing the data you want to send.
1import org.springframework.web.socket.TextMessage;
2import org.springframework.web.socket.WebSocketSession;
3import org.springframework.web.socket.handler.TextWebSocketHandler;
4import org.springframework.web.socket.CloseStatus;
5
6public class MyWebSocketHandler extends TextWebSocketHandler {
7
8    @Override
9    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
10        System.out.println("Connection established: " + session.getId());
11    }
12
13    @Override
14    public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
15        String receivedMessage = message.getPayload();
16        System.out.println("Received: " + receivedMessage);
17        TextMessage response = new TextMessage("Server received: " + receivedMessage);
18        session.sendMessage(response);
19    }
20
21    @Override
22    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
23        System.out.println("Connection closed: " + session.getId() + " with status " + status);
24    }
25}
26
The afterConnectionEstablished method is called when a new WebSocket connection is established. The afterConnectionClosed method is called when a WebSocket connection is closed. These methods allow you to manage resources or perform cleanup tasks when connections are established or terminated.

Introducing STOMP for Advanced WebSocket Messaging

STOMP (Simple Text Oriented Messaging Protocol) is a messaging protocol that adds a layer of semantics on top of WebSockets. It defines how messages are exchanged between clients and servers, including concepts like destinations, subscriptions, and message brokers. STOMP simplifies the development of complex real-time applications by providing a standardized way to handle messaging.
By using STOMP, you can decouple the client and server, making it easier to manage and scale your application. Clients subscribe to specific destinations, and the server sends messages to those destinations. This allows for more flexible and efficient message routing.

Configuring Spring Boot for STOMP over WebSocket

To use STOMP with Spring Boot, you need to add the spring-messaging dependency to your project. You also need to use the @EnableWebSocketMessageBroker annotation in your configuration class.
1<!-- pom.xml dependencies -->
2<dependency>
3    <groupId>org.springframework.boot</groupId>
4    <artifactId>spring-boot-starter-websocket</artifactId>
5</dependency>
6<dependency>
7    <groupId>org.springframework.boot</groupId>
8    <artifactId>spring-boot-starter-amqp</artifactId>
9</dependency>
10<dependency>
11    <groupId>org.springframework.boot</groupId>
12    <artifactId>spring-messaging</artifactId>
13</dependency>
14
1import org.springframework.context.annotation.Configuration;
2import org.springframework.messaging.simp.config.MessageBrokerRegistry;
3import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
4import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
5import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
6
7@Configuration
8@EnableWebSocketMessageBroker
9public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
10
11    @Override
12    public void configureMessageBroker(MessageBrokerRegistry config) {
13        config.enableSimpleBroker("/topic");
14        config.setApplicationDestinationPrefixes("/app");
15    }
16
17    @Override
18    public void registerStompEndpoints(StompEndpointRegistry registry) {
19        registry.addEndpoint("/gs-guide-websocket").withSockJS();
20    }
21
22}
23
The @EnableWebSocketMessageBroker annotation enables WebSocket message handling, backed by a message broker. The configureMessageBroker method configures the message broker. enableSimpleBroker("/topic") enables a simple in-memory broker that relays messages to destinations prefixed with /topic. setApplicationDestinationPrefixes("/app") configures the prefix for messages that are handled by application code.
The registerStompEndpoints method registers the /gs-guide-websocket endpoint, enabling SockJS fallback options.

Creating STOMP Message Handling Endpoints

To handle STOMP messages, you can use the @MessageMapping annotation to define message handling methods. The @SendTo annotation specifies the destination for sending responses.
1import org.springframework.messaging.handler.annotation.MessageMapping;
2import org.springframework.messaging.handler.annotation.SendTo;
3import org.springframework.stereotype.Controller;
4
5@Controller
6public class GreetingController {
7
8    @MessageMapping("/hello")
9    @SendTo("/topic/greetings")
10    public String greeting(String message) throws Exception {
11        Thread.sleep(1000);
12        return "Hello, " + message + "!";
13    }
14
15}
16
The @Controller annotation marks the class as a controller. The @MessageMapping("/hello") annotation ensures that if a message is sent to destination /app/hello, then the greeting() method is invoked. The @SendTo("/topic/greetings") annotation ensures that if a message is returned from the greeting() method, then the message is sent to the /topic/greetings destination.

Using SockJS for Browser Compatibility with Spring Boot WebSocket

SockJS is a JavaScript library that provides a fallback mechanism for WebSocket communication in browsers that don't fully support WebSockets. It emulates WebSocket using other techniques, such as HTTP long-polling, to ensure compatibility across different browsers.
To configure SockJS in Spring Boot, you need to modify the WebSocket configuration to enable SockJS fallback options. In the registerStompEndpoints method, use the withSockJS() method to enable SockJS support.
1@Override
2public void registerStompEndpoints(StompEndpointRegistry registry) {
3    registry.addEndpoint("/gs-guide-websocket").withSockJS();
4}
5

Enhancing Spring Boot WebSocket Security

Securing WebSocket connections is crucial to protect your application from unauthorized access. Spring Security can be integrated with WebSocket to authenticate users and authorize access to endpoints.
You can configure interceptors to handle authentication and authorization. These interceptors can check user credentials and permissions before allowing access to WebSocket endpoints. A deeper dive into securing Spring Boot WebSocket applications will be covered in a future dedicated article.

Building a Simple Chat Application with Spring Boot WebSocket

Let's build a basic chat application using Spring Boot WebSocket with STOMP.
1// Server-side Chat Controller
2import org.springframework.messaging.handler.annotation.MessageMapping;
3import org.springframework.messaging.handler.annotation.SendTo;
4import org.springframework.stereotype.Controller;
5
6@Controller
7public class ChatController {
8
9    @MessageMapping("/chat.sendMessage")
10    @SendTo("/topic/public")
11    public ChatMessage sendMessage(ChatMessage message) {
12        return message;
13    }
14
15    @MessageMapping("/chat.addUser")
16    @SendTo("/topic/public")
17    public ChatMessage addUser(ChatMessage message) {
18        return message;
19    }
20
21}
22
1//ChatMessage.java
2public class ChatMessage {
3    private String type;
4    private String content;
5    private String sender;
6    // Getters and setters
7    public String getType() {
8        return type;
9    }
10
11    public void setType(String type) {
12        this.type = type;
13    }
14
15    public String getContent() {
16        return content;
17    }
18
19    public void setContent(String content) {
20        this.content = content;
21    }
22
23    public String getSender() {
24        return sender;
25    }
26
27    public void setSender(String sender) {
28        this.sender = sender;
29    }
30}
31
1// Client-side JavaScript for Chat
2
3var stompClient = null;
4
5function connect() {
6    var socket = new SockJS('/gs-guide-websocket');
7    stompClient = Stomp.over(socket);
8    stompClient.connect({}, function (frame) {
9        console.log('Connected: ' + frame);
10        stompClient.subscribe('/topic/public', function (message) {
11            showMessage(JSON.parse(message.body).content);
12        });
13    });
14}
15
16function disconnect() {
17    if (stompClient !== null) {
18        stompClient.disconnect();
19    }
20    console.log("Disconnected");
21}
22
23function sendMessage(message) {
24    stompClient.send("/app/chat.sendMessage", {}, JSON.stringify({'content': message, 'type': 'CHAT'}));
25}
26
27function showMessage(message) {
28    var p = document.createElement('p');
29    p.style.wordWrap = 'break-word';
30    p.appendChild(document.createTextNode(message));
31    document.getElementById('messages').appendChild(p);
32}
33
This simplified chat application allows users to send and receive messages in a public chat room.
1sequenceDiagram
2    participant Client
3    participant Server
4    participant MessageBroker
5
6    Client->>Server: Connect to WebSocket Endpoint
7    Server->>Client: Establish WebSocket Connection
8
9    Client->>Server: Subscribe to Topic (e.g., /topic/messages)
10    Server->>MessageBroker: Relay Subscription Request
11    MessageBroker->>Server: Acknowledge Subscription
12    Server->>Client: Acknowledge Subscription
13
14    Client->>Server: Send Message to Destination (e.g., /app/sendMessage)
15    Server->>MessageBroker: Route Message to Topic /topic/messages
16    MessageBroker->>Server: Acknowledge Routing
17    Server->>Client: Acknowledge Message
18
19    MessageBroker->>Server: Push Message to Subscribers of /topic/messages
20    Server->>Client: Send Message to Client
21

Testing Your Spring Boot WebSocket Application

Testing your Spring Boot WebSocket application is crucial to ensure its reliability. You can use tools like Postman (with WebSocket support), online WebSocket clients, or write integration tests using Spring's testing framework.
Testing scenarios should include connection establishment, message sending/receiving, error handling, and proper disconnection.

Conclusion: Embracing Real-time with Spring Boot WebSocket

Spring Boot provides a powerful and convenient way to develop real-time applications using WebSockets. With its support for STOMP and SockJS, you can build sophisticated messaging systems that work across a variety of browsers and platforms. By following the steps outlined in this article, you can easily integrate WebSockets into your Spring Boot projects and create engaging real-time experiences for your users. Explore further and experiment with real-time applications.
Further Resources:

Get 10,000 Free Minutes Every Months

No credit card required to start.

Want to level-up your learning? Subscribe now

Subscribe to our newsletter for more tech based insights

FAQ