Real-Time Notifications with WebSocket in Spring Boot
Tags: webdev, programming, tutorial, java
Published: 2024-06-01 17:22:54

In modern web applications, real-time communication between the server and clients is becoming increasingly important. Traditional HTTP request-response communication is not suitable for scenarios where data needs to be pushed from the server to clients in real-time. This is where WebSocket comes into play. WebSocket is a protocol that provides full-duplex communication channels over a single TCP connection, allowing for bi-directional data flow between the server and clients.

Spring Boot provides excellent support for WebSocket through its `spring-websocket` module. In this blog post, we'll explore how to build a real-time notification system using WebSocket in a Spring Boot application.

## Prerequisites

Before we dive in, ensure you have the following:
- Basic knowledge of Spring Boot and Java
- Understanding of the WebSocket protocol
- Familiarity with front-end technologies like JavaScript and HTML

## Setting Up the Spring Boot Application

### 1. Add Dependencies

First, create a new Spring Boot project or add the following dependencies to your existing `pom.xml`:

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
```

### 2. Enable WebSocket Support

Enable WebSocket support by adding the `@EnableWebSocketMessageBroker` annotation to your main application class or a dedicated configuration class.

## Creating the WebSocket Configuration

### 1. Define WebSocket Configuration

Create a configuration class to set up WebSocket:

```java
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.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").withSockJS();
    }
}
```

### 2. Explanation

- `@EnableWebSocketMessageBroker` enables WebSocket message handling.
- `setApplicationDestinationPrefixes` maps incoming messages to `@MessageMapping` methods.
- `enableSimpleBroker` sets up an in-memory message broker.

## Implementing the WebSocket Handler

### 1. Create a Message Controller

Create a controller to handle incoming WebSocket messages:

```java
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class NotificationController {

    @MessageMapping("/send")
    @SendTo("/topic/notifications")
    public String sendNotification(String message) {
        return message;
    }
}
```

### 2. Explanation

- `@MessageMapping("/send")` maps to `/app/send`.
- `@SendTo("/topic/notifications")` sends the message to `/topic/notifications`.

## Sending Notifications

### 1. Inject SimpMessagingTemplate

Inject `SimpMessagingTemplate` to send messages:

```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    @Autowired
    private SimpMessagingTemplate template;

    public void sendNotification(String message) {
        template.convertAndSend("/topic/notifications", message);
    }
}
```

### 2. Explanation

- `convertAndSend` sends a message to the specified destination.

## Building the Front-End

### 1. Create HTML File

Create an HTML file with a WebSocket connection:

```html
<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Notifications</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.1/sockjs.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
</head>
<body>
    <div id="notifications"></div>
    <script>
        var socket = new SockJS('/websocket');
        var stompClient = Stomp.over(socket);

        stompClient.connect({}, function(frame) {
            stompClient.subscribe('/topic/notifications', function(notification) {
                var notifications = document.getElementById('notifications');
                var message = document.createElement('p');
                message.appendChild(document.createTextNode(notification.body));
                notifications.appendChild(message);
            });
        });
    </script>
</body>
</html>
```

### 2. Explanation

- Connect to the WebSocket endpoint.
- Subscribe to `/topic/notifications`.
- Display received messages in the browser.

## Example Implementation

Here is a complete example implementation with the server-side and client-side components we discussed.

### Server-Side Code

#### `WebSocketConfig.java`

```java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").withSockJS();
    }
}
```

#### `NotificationController.java`

```java
@Controller
public class NotificationController {

    @MessageMapping("/send")
    @SendTo("/topic/notifications")
    public String sendNotification(String message) {
        return message;
    }
}
```

#### `NotificationService.java`

```java
@Service
public class NotificationService {

    @Autowired
    private SimpMessagingTemplate template;

    public void sendNotification(String message) {
        template.convertAndSend("/topic/notifications", message);
    }
}
```

### Client-Side Code

#### `index.html`

```html
<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Notifications</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.1/sockjs.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
</head>
<body>
    <div id="notifications"></div>
    <script>
        var socket = new SockJS('/websocket');
        var stompClient = Stomp.over(socket);

        stompClient.connect({}, function(frame) {
            stompClient.subscribe('/topic/notifications', function(notification) {
                var notifications = document.getElementById('notifications');
                var message = document.createElement('p');
                message.appendChild(document.createTextNode(notification.body));
                notifications.appendChild(message);
            });
        });
    </script>
</body>
</html>
```

## Conclusion

In this blog post, we learned how to implement real-time notifications using WebSocket in a Spring Boot application. We covered the setup, configuration, and implementation of the server-side and client-side components. WebSocket provides a powerful mechanism for real-time communication, enabling applications to deliver timely and relevant notifications to users.

By following this guide, you can create a robust real-time notification system that enhances user experience and engagement in your web applications.