Integrating WebSocket in Spring Boot: Javax, WebMVC, WebFlux and Third‑Party Libraries

This article provides a comprehensive guide on implementing WebSocket in Spring Boot, covering the Javax API, Spring WebMVC and WebFlux configurations, client and server setups, message handling, handshake interceptors, and a brief overview of popular third‑party libraries such as Java‑WebSocket, SocketIO and Netty.

Top Architect
Top Architect
Top Architect
Integrating WebSocket in Spring Boot: Javax, WebMVC, WebFlux and Third‑Party Libraries

Introduction

The author, a senior architect, shares research on various WebSocket integration methods after publishing a library for Spring Cloud configuration annotations.

Supported Integration Methods

Javax

WebMVC

WebFlux

Java‑WebSocket

SocketIO

Netty

The article focuses on the first three methods because they are the most common in Spring Boot projects, while the latter three are presented as brief introductions.

Javax (javax.websocket)

Server Side

Step 1

@Component
@ServerEndpoint("/websocket/{type}")
public class JavaxWebSocketServerEndpoint {
    @OnOpen
    public void onOpen(Session session, EndpointConfig config, @PathParam("type") String type) { /* connection opened */ }
    @OnClose
    public void onClose(Session session, CloseReason reason) { /* connection closed */ }
    @OnMessage
    public void onMessage(Session session, String message) { /* text message */ }
    @OnMessage
    public void onMessage(Session session, PongMessage message) { /* pong */ }
    @OnMessage
    public void onMessage(Session session, ByteBuffer message) { /* binary */ }
    @OnError
    public void onError(Session session, Throwable e) { /* error handling */ }
}

The @ServerEndpoint annotation marks the class as a WebSocket endpoint, and the path can contain dynamic variables.

Step 2

implementation 'org.springframework.boot:spring-boot-starter-websocket'

@Configuration(proxyBeanMethods = false)
public class JavaxWebSocketConfiguration {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

Adding the Spring WebSocket starter and manually registering ServerEndpointExporter enables the native Javax usage.

Cold Knowledge

javax.websocket defines PongMessage but not PingMessage.

Most WebSocket implementations automatically reply to ping with pong, so custom ping handling is rarely needed.

Sending ping/pong via session.getAsyncRemote().sendPing(...) and sendPong(...).

Client Side

Step 1

@ClientEndpoint
public class JavaxWebSocketClientEndpoint {
    @OnOpen
    public void onOpen(Session session) { /* connection opened */ }
    @OnClose
    public void onClose(Session session, CloseReason reason) { /* connection closed */ }
    @OnMessage
    public void onMessage(Session session, String message) { /* text */ }
    @OnMessage
    public void onMessage(Session session, PongMessage message) { /* pong */ }
    @OnMessage
    public void onMessage(Session session, ByteBuffer message) { /* binary */ }
    @OnError
    public void onError(Session session, Throwable e) { /* error */ }
}

The client uses @ClientEndpoint and the same callback annotations as the server.

Step 2

WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(JavaxWebSocketClientEndpoint.class, uri);

Obtaining a WebSocketContainer via ContainerProvider (SPI) and connecting to the server.

Cold Knowledge

In Spring environments, ServletContextAware can be used to retrieve the container from the servlet context.

@Component
public class JavaxWebSocketContainer implements ServletContextAware {
    private volatile WebSocketContainer container;
    public WebSocketContainer getContainer() { /* lazy init */ }
    @Override
    public void setServletContext(@NonNull ServletContext servletContext) { /* obtain from attribute */ }
}

Sending Messages

// Example of sending different message types
session.getAsyncRemote().sendText("Hello");
session.getAsyncRemote().sendBinary(ByteBuffer.wrap(data));
session.getAsyncRemote().sendObject(obj);
session.getAsyncRemote().sendPing(ByteBuffer.allocate(0));
session.getAsyncRemote().sendPong(ByteBuffer.allocate(0));

WebMVC Integration

Server Side

Step 1

public class ServletWebSocketServerHandler implements WebSocketHandler {
    @Override
    public void afterConnectionEstablished(@NonNull WebSocketSession session) { /* ... */ }
    @Override
    public void handleMessage(@NonNull WebSocketSession session, @NonNull WebSocketMessage<?> message) { /* ... */ }
    @Override
    public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable ex) { /* ... */ }
    @Override
    public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) { /* ... */ }
    @Override
    public boolean supportsPartialMessages() { return false; }
}

Step 2

@Configuration
@EnableWebSocket
public class ServletWebSocketServerConfigurer implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
        registry.addHandler(new ServletWebSocketServerHandler(), "/websocket")
                .setAllowedOrigins("*");
    }
}

Enabling WebSocket with @EnableWebSocket and registering the handler.

Handshake Interceptor

public class ServletWebSocketHandshakeInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                   WebSocketHandler wsHandler, Map<String, Object> attributes) {
        // return true to continue, false to abort
        return false;
    }
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
                               WebSocketHandler wsHandler, Exception ex) { /* ... */ }
}

Adding the interceptor via .addInterceptors(new ServletWebSocketHandshakeInterceptor()).

Cold Knowledge

Default WebSocketHandlerRegistry stores handlers in a Map<String, WebSocketHandler>, which does not support Ant‑style path patterns; a custom UrlPathHelper can be used to achieve wildcard matching.

Client Side (WebMVC)

public class ServletWebSocketClientHandler implements WebSocketHandler { /* same callbacks as server */ }

WebSocketClient client = new StandardWebSocketClient();
WebSocketHandler handler = new ServletWebSocketClientHandler();
WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler, uri);
manager.start();

Choosing the appropriate WebSocketClient implementation (Standard, Jetty, etc.) based on the runtime container.

WebFlux Integration

Server Side

public class ReactiveWebSocketServerHandler implements WebSocketHandler {
    @Override
    public Mono<Void> handle(WebSocketSession session) {
        Mono<Void> send = session.send(Flux.create(sink -> {
            // sink.next(message) to push data
        })).doOnError(...);
        Mono<Void> receive = session.receive()
                .doOnNext(...)
                .doOnError(...)
                .then();
        Disposable close = session.closeStatus()
                .doOnError(...)
                .subscribe(...);
        return Mono.zip(send, receive).then();
    }
}

Handler Mapping

@Component
public class ReactiveWebSocketServerHandlerMapping extends SimpleUrlHandlerMapping {
    public ReactiveWebSocketServerHandlerMapping() {
        Map<String, WebSocketHandler> map = new HashMap<>();
        map.put("/websocket/**", new ReactiveWebSocketServerHandler());
        setUrlMap(map);
        setOrder(100);
    }
}

Configuration

@Configuration(proxyBeanMethods = false)
public class ReactiveWebSocketConfiguration {
    @Bean
    public WebSocketHandlerAdapter webSocketHandlerAdapter() {
        return new WebSocketHandlerAdapter();
    }
}

Cold Knowledge

The custom HandlerMapping must have a higher order than other mappings; otherwise the WebSocket endpoint may be shadowed.

Client Side (WebFlux)

WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketHandler handler = new ReactiveWebSocketClientHandler();
client.execute(uri, handler).subscribe();

Similar to the server, the client uses a reactive WebSocketClient implementation appropriate for the runtime (Reactor Netty, Jetty, Undertow, etc.).

Third‑Party Libraries

Java‑WebSocket : pure Java library with >9k stars on GitHub.

SocketIO : protocol‑wrapped library offering multi‑language support, suited for chat‑type applications.

Netty : widely used asynchronous networking framework; extensive documentation and examples are available.

Each library provides its own server and client APIs; choose based on project requirements.

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.

JavaSpring BootWebSocket
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.