Master Real-Time Push: Spring Boot WebSocket Integration Guide

This article walks through building a real‑time push system with Spring Boot by integrating WebSocket and MQTT, covering the protocol basics, Maven setup, configuration classes, server implementation, controller endpoints, front‑end testing page, common pitfalls, and deployment tips.

Programmer DD
Programmer DD
Programmer DD
Master Real-Time Push: Spring Boot WebSocket Integration Guide

Introduction

During a project that used Netty and MQTT for message exchange, the backend needed to actively push messages to the frontend, so WebSocket was employed.

What is WebSocket?

WebSocket is a TCP‑based protocol that enables full‑duplex communication between client and server, allowing the server to send data without a client request—ideal for push notifications and multi‑user chat.

Why not HTTP?

HTTP is a half‑duplex protocol where the client must initiate every request, making it unsuitable for server‑initiated pushes.

Steps

1. Add Dependency

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

2. Enable Spring Boot WebSocket Support

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3. Core Configuration: WebSocketServer

The server acts as a WebSocket controller, using @ServerEndpoint to define the URL and managing connections with a thread‑safe CopyOnWriteArraySet.

@ServerEndpoint marks the class as a WebSocket endpoint and binds a URL for client connections.

A ConcurrentHashMap (or similar) stores sessions keyed by user ID for targeted pushes.

package cc.mrbird.febs.external.webScoket;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/api/websocket/{sid}")
public class WebSocketServer {
    private static int onlineCount = 0;
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
    private Session session;
    private String sid = "";

    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);
        this.sid = sid;
        addOnlineCount();
        try { sendMessage("conn_success"); } catch (IOException e) { e.printStackTrace(); }
    }

    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        for (WebSocketServer item : webSocketSet) {
            try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); }
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        for (WebSocketServer item : webSocketSet) {
            if (sid == null) {
                // broadcast
            } else if (item.sid.equals(sid)) {
                item.sendMessage(message);
            }
        }
    }

    public static synchronized int getOnlineCount() { return onlineCount; }
    public static synchronized void addOnlineCount() { onlineCount++; }
    public static synchronized void subOnlineCount() { onlineCount--; }
    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() { return webSocketSet; }
}

4. Test Controller

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Controller("web_Scoket_system")
@RequestMapping("/api/socket")
public class SystemController {
    @GetMapping("/index/{userId}")
    public ModelAndView socket(@PathVariable String userId) {
        ModelAndView mav = new ModelAndView("/socket1");
        mav.addObject("userId", userId);
        return mav;
    }

    @ResponseBody
    @RequestMapping("/socket/push/{cid}")
    public Map<String,Object> pushToWeb(@PathVariable String cid, String message) {
        Map<String,Object> result = new HashMap<>();
        try { WebSocketServer.sendInfo(message, cid); result.put("code", cid); result.put("msg", message); }
        catch (IOException e) { e.printStackTrace(); }
        return result;
    }
}

5. Test Page (index.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Java Backend WebSocket Demo</title>
    <script src="js/jquery.min.js"></script>
</head>
<body>
    <div id="main" style="width:1200px;height:800px;"></div>
    Welcome<br/>
    <input id="text" type="text"/>
    <button onclick="send()">Send Message</button>
    <hr/>
    <button onclick="closeWebSocket()">Close WebSocket</button>
    <hr/>
    <div id="message"></div>
    <script type="text/javascript">
        var websocket = null;
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://192.168.100.196:8082/api/websocket/100");
        } else { alert('Browser does not support WebSocket'); }
        websocket.onerror = function(){ setMessageInnerHTML('WebSocket error'); };
        websocket.onopen = function(){ setMessageInnerHTML('WebSocket connected'); };
        websocket.onmessage = function(event){ console.log(event); setMessageInnerHTML(event.data); };
        websocket.onclose = function(){ setMessageInnerHTML('WebSocket closed'); };
        window.onbeforeunload = function(){ closeWebSocket(); };
        function setMessageInnerHTML(msg){ document.getElementById('message').innerHTML += msg + '<br/>'; }
        function closeWebSocket(){ websocket.close(); }
        function send(){
            var message = document.getElementById('text').value;
            websocket.send('{"msg":"' + message + '"}');
            setMessageInnerHTML(message);
        }
    </script>
</body>
</html>

6. Result Display

Backend logs show connection requests and online user count; the front‑end page displays messages in real time.

Front‑end screenshot:

Conclusion

A common issue is that the WebSocket endpoint is initialized before the Spring container, causing NullPointerException when injecting services. The fix is to statically initialize required services inside the WebSocketServer class.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentSpring BootWebSocketreal-time communicationMQTT
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.