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/
).- Visit the Spring Initializr website.
- Choose your project's metadata (e.g., Maven or Gradle, Java version).
- Add the
spring-boot-starter-websocket
dependency. - Generate the project and download the ZIP file.
- 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:
Spring Framework WebSocket Documentation
- Learn more about the WebSocket support offered by the Spring Framework.
WebSocket Protocol RFC
- Read the official WebSocket protocol specification.
SockJS Client Library
- Explore the SockJS client library for browser compatibility.
Want to level-up your learning? Subscribe now
Subscribe to our newsletter for more tech based insights
FAQ