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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
