Integrating WebSocket in Spring Boot for Real‑Time Server Push

This tutorial demonstrates how to integrate Spring Boot with WebSocket to achieve server‑initiated real‑time message pushing, covering dependency setup, configuration, server implementation, controller and client page code, and troubleshooting initialization order issues.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Integrating WebSocket in Spring Boot for Real‑Time Server Push

Hello, I'm Lei Ge. In a project we used Netty and MQTT, needing the backend to actively push received messages to the frontend, so we employed MQTT and recorded the process.

WebSocket is a TCP‑based protocol that provides full‑duplex communication, allowing the server to push information to the client without a client request, which is ideal for push notifications and multi‑user chat.

We integrate WebSocket with Spring Boot. The project structure is shown in the original article (images omitted).

Step 1: Add Maven dependency

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

Step 2: Enable 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();
    }
}

Step 3: Core configuration – WebSocketServer

The class is annotated with @ServerEndpoint to act as a WebSocket controller, manages sessions, online count, and provides methods for broadcasting and targeted pushes.

package cc.mrbird.febs.external.webScoket;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

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

@Component
@Slf4j
@Service
@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");
            log.info("New connection: " + sid + ", online count: " + getOnlineCount());
        } catch (IOException e) {
            log.error("WebSocket IO Exception");
        }
    }

    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
        log.info("Connection closed for sid: " + sid);
        log.info("Current online count: " + getOnlineCount());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("Received from sid " + sid + ": " + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("Error occurred");
        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 {
        log.info("Push to sid " + sid + ", content: " + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                if (sid == null) {
                    // broadcast (code omitted)
                } else if (item.sid.equals(sid)) {
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    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; }
}

Step 4: Test Controller

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
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 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;
    }
}

Step 5: Test Page (index.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Java Backend WebSocket Demo</title>
    <script type="text/javascript" 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>
</body>
<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('Current 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); };
    websocket.onclose = function() { setMessageInnerHTML("WebSocket closed"); };
    window.onbeforeunload = function() { closeWebSocket(); };
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
    function closeWebSocket() { websocket.close(); }
    function send() {
        var message = document.getElementById('text').value;
        websocket.send('{"msg":"' + message + '"}');
        setMessageInnerHTML(message + "
");
    }
</script>
</html>

Step 6: Result Display

Backend logs show connection handling and online count; frontend page displays received messages and allows sending new ones (screenshots omitted).

Summary

During startup the WebSocket server is initialized before the Spring container, causing a NullPointerException when trying to inject services; the fix is to statically initialize required services in the WebSocketServer and add the necessary configuration (shown in the original images).

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 DevelopmentWebSocketreal-time communication
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.