Integrating WebSocket in Spring Boot: Javax, WebMVC, WebFlux, Java‑WebSocket, SocketIO and Netty
This article provides a comprehensive guide on implementing WebSocket in Spring Boot, covering three native approaches (Javax, WebMVC, WebFlux) with server‑ and client‑side configurations, and briefly introducing four third‑party libraries (Java‑WebSocket, SocketIO, Netty) for advanced use cases.
Javax
The javax.websocket package defines the standard WebSocket API. A server endpoint is created by annotating a class with @ServerEndpoint and implementing lifecycle callbacks such as @OnOpen , @OnClose , @OnMessage and @OnError . The following code shows a typical server implementation:
@Component
@ServerEndpoint("/websocket/{type}")
public class JavaxWebSocketServerEndpoint {
@OnOpen
public void onOpen(Session session, EndpointConfig config,
@PathParam(value = "type") String type) {
// connection established
}
@OnClose
public void onClose(Session session, CloseReason reason) {
// connection closed
}
@OnMessage
public void onMessage(Session session, String message) {
// receive text message
}
@OnMessage
public void onMessage(Session session, PongMessage message) {
// receive pong message
}
@OnMessage
public void onMessage(Session session, ByteBuffer message) {
// receive binary message
}
@OnError
public void onError(Session session, Throwable e) {
// error handling
}
}Spring Boot needs the WebSocket starter and a ServerEndpointExporter bean to enable the native API:
implementation 'org.springframework.boot:spring-boot-starter-websocket'
@Configuration(proxyBeanMethods = false)
public class JavaxWebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}Key notes: the library defines PongMessage but not PingMessage ; the server automatically replies to ping frames, so custom ping handling is rarely needed.
Client
The client side also uses annotations. A typical client endpoint looks like this:
@ClientEndpoint
public class JavaxWebSocketClientEndpoint {
@OnOpen
public void onOpen(Session session) {
// connection established
}
@OnClose
public void onClose(Session session, CloseReason reason) {
// connection closed
}
@OnMessage
public void onMessage(Session session, String message) {
// receive text message
}
@OnMessage
public void onMessage(Session session, PongMessage message) {
// receive pong message
}
@OnMessage
public void onMessage(Session session, ByteBuffer message) {
// receive binary message
}
@OnError
public void onError(Session session, Throwable e) {
// error handling
}
}Connecting is done via ContainerProvider.getWebSocketContainer() :
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(JavaxWebSocketClientEndpoint.class, uri);In a Spring environment, ServletContextAware can be used to obtain the container.
@Component
public class JavaxWebSocketContainer implements ServletContextAware {
private volatile WebSocketContainer container;
public WebSocketContainer getContainer() {
if (container == null) {
synchronized (this) {
if (container == null) {
container = ContainerProvider.getWebSocketContainer();
}
}
}
return container;
}
@Override
public void setServletContext(@NonNull ServletContext servletContext) {
if (container == null) {
container = (WebSocketContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer");
}
}
}Sending messages is straightforward using the Session API:
Session session = ...;
session.getAsyncRemote().sendText("message");
session.getAsyncRemote().sendBinary(ByteBuffer.wrap(data));
session.getAsyncRemote().sendObject(obj);
session.getAsyncRemote().sendPing(ByteBuffer.allocate(0));
session.getAsyncRemote().sendPong(ByteBuffer.allocate(0));WebMVC
Spring MVC requires the same WebSocket starter. A handler implements WebSocketHandler :
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
public class ServletWebSocketServerHandler implements WebSocketHandler {
@Override
public void afterConnectionEstablished(@NonNull WebSocketSession session) {
// connection established
}
@Override
public void handleMessage(@NonNull WebSocketSession session, @NonNull WebSocketMessage
message) {
// receive message
}
@Override
public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable exception) {
// error handling
}
@Override
public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus closeStatus) {
// connection closed
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}Register the handler with @EnableWebSocket and a WebSocketConfigurer implementation:
@Configuration
@EnableWebSocket
public class ServletWebSocketServerConfigurer implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
registry.addHandler(new ServletWebSocketServerHandler(), "/websocket")
.setAllowedOrigins("*");
}
}Handshake interception can be added by implementing HandshakeInterceptor :
registry.addHandler(new ServletWebSocketServerHandler(), "/websocket")
.addInterceptors(new ServletWebSocketHandshakeInterceptor())
.setAllowedOrigins("*");
public static class ServletWebSocketHandshakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map
attributes) {
// return true to continue handshake, false to abort
return false;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
// after handshake logic
}
}Note: the default path matcher does not support Ant‑style patterns; a custom UrlPathHelper can be used to enable /websocket/** matching.
WebFlux
Reactive WebSocket support is built‑in; no extra dependency is required. The server handler implements org.springframework.web.reactive.socket.WebSocketHandler :
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession;
public class ReactiveWebSocketServerHandler implements WebSocketHandler {
@NonNull
@Override
public Mono<Void> handle(WebSocketSession session) {
Mono<Void> send = session.send(Flux.create(sink -> {
// push messages via sink
sink.next(session.textMessage("hello"));
})).doOnError(e -> {/* error handling */});
Mono<Void> receive = session.receive()
.doOnNext(msg -> {/* process incoming */})
.doOnError(e -> {/* error handling */})
.then();
return Mono.zip(send, receive).then();
}
}Register the handler with a SimpleUrlHandlerMapping and enable the reactive adapter:
@Component
public class ReactiveWebSocketServerHandlerMapping extends SimpleUrlHandlerMapping {
public ReactiveWebSocketServerHandlerMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/websocket/**", new ReactiveWebSocketServerHandler());
setUrlMap(map);
setOrder(100);
}
}
@Configuration(proxyBeanMethods = false)
public class ReactiveWebSocketConfiguration {
@Bean
public WebSocketHandlerAdapter webSocketHandlerAdapter() {
return new WebSocketHandlerAdapter();
}
}Client side uses a reactive WebSocketClient such as ReactorNettyWebSocketClient :
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketHandler handler = new ReactiveWebSocketClientHandler();
client.execute(uri, handler).subscribe();Java‑WebSocket (Third‑party library)
This pure‑Java library (GitHub: TooTallNate/Java-WebSocket ) provides a lightweight WebSocket implementation with extensive documentation and over 9k stars.
SocketIO
Socket.IO adds a custom protocol on top of WebSocket, offering multi‑language support and higher‑level features such as rooms and acknowledgments. Use socket.io-server-java and socket.io-client-java for server and client implementations.
Netty
Netty is a widely used asynchronous event‑driven network framework that also supports WebSocket. Numerous examples and documentation are available on its GitHub repository.
Overall, the article walks through three native Spring Boot WebSocket integrations (Javax, WebMVC, WebFlux) with detailed server and client code, and then briefly surveys four popular third‑party libraries for more specialized scenarios.
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.