WebSockets en Java con Spring Boot: chat en tiempo real
Aprende WebSockets en Java con Spring Boot: dependencia, configuración STOMP con SockJS y un chat en tiempo real con ejemplos de código listos para adaptar.

Spring Boot ofrece soporte nativo para WebSockets con el starter spring-boot-starter-websocket, STOMP y SockJS. Con unas pocas anotaciones como @EnableWebSocketMessageBroker y @MessageMapping construyes un chat en tiempo real donde el servidor empuja mensajes a todos los clientes conectados sin recargas de página.
WebSockets en Java con Spring Boot: chat en tiempo real
Última actualización: agosto 2026
Contenido Rápido
- WebSocket es un protocolo bidireccional y persistente sobre TCP
spring-boot-starter-websocketaporta todo el soporte de Spring Boot- STOMP organiza los mensajes en destinos como
/topicy/app - SockJS garantiza compatibilidad con navegadores sin WebSocket
@MessageMappingy@SendToconectan el controlador con el broker- El patrón de la guía oficial de Spring sirve para chats y notificaciones
Cuando una página necesita recibir datos sin que el usuario haga nada, la solución clásica era preguntar cada pocos segundos. Los WebSockets cambian ese modelo: abren un canal bidireccional y permanente en el que el servidor envía mensajes cuando quiera. Spring Boot integra el protocolo casi automáticamente, y en esta guía montas un chat en tiempo real con la configuración mínima. Si es tu primera vez con el framework, empieza por qué es Spring Boot y cómo funciona.
El ejemplo sigue la guía oficial de Spring: un mensaje con usuario y contenido, un destino público y todos los suscritos reciben la copia al instante. Antes de escribir código conviene saber en qué se diferencia WebSocket de las alternativas HTTP y qué papel juegan STOMP y SockJS.
Qué es WebSocket y cuándo usarlo
WebSocket es un protocolo full-duplex que arranca como una petición HTTP normal y, mediante la cabecera Upgrade, pasa a una conexión TCP persistente. Una vez establecida, ambos extremos envían y reciben mensajes sin abrir una petición por cada dato. Es la elección natural para chats, juegos, editores colaborativos y pantallas que reflejan cambios en milisegundos.
Existe un post hermano sobre WebSockets en Java con la API pura, la especificación jakarta.websocket sin Spring. Este artículo es la otra cara: Spring Boot, que resuelve sesiones, broker de mensajes y compatibilidad con navegadores antiguos para que te centres en la lógica del chat.
Añadir la dependencia de WebSocket
Spring Boot agrupa todo el soporte en el starter spring-boot-starter-websocket. Lo declaras en Maven sin versionar; el BOM de Spring Boot gestiona la versión compatible:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
En Gradle la equivalencia es implementation 'org.springframework.boot:spring-boot-starter-websocket'. El starter trae la API de WebSocket, el soporte STOMP y el cliente Java; con eso basta para el ejemplo.
El mensaje: un record ChatMessage
Como el chat envía y recibe el mismo tipo de dato, define el mensaje como un record de Java con tres campos: tipo de evento (CHAT, JOIN o LEAVE), usuario y contenido:
public record ChatMessage(String tipo, String usuario, String contenido) {
}
El record aporta constructor, accesores y equals, y Jackson lo serializa a JSON y lo reconstruye al viajar por el WebSocket. Al ser inmutable, una sesión nunca modifica el mensaje de otra.
¿Quieres probarlo sin instalar nada? La parte de Java puro de este ejemplo, como el record ChatMessage, puedes ejecutarla en el Java Playground de CodeJa. El chat completo necesita un servidor Spring con STOMP, así que este fragmento es lo que puedes practicar en el navegador.
Configurar el broker de mensajes
El siguiente paso es la configuración. @EnableWebSocketMessageBroker activa el broker de mensajes y los endpoints STOMP. configureMessageBroker define los prefijos: /topic para suscripciones y /app para envíos de la aplicación. registerStompEndpoints publica el endpoint /chat y lo envuelve en SockJS para navegadores sin soporte nativo:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
}
Este fragmento depende de Spring y corre dentro de una aplicación Boot. El broker simple en memoria basta para desarrollo; cuando la aplicación crezca, sustitúyelo por un broker externo como RabbitMQ o ActiveMQ sin tocar el controlador.
El controlador del chat
El controlador es donde se escribe la lógica. @MessageMapping("/chat.sendMessage") recibe los mensajes enviados al prefijo /app y @SendTo("/topic/public") los retransmite a todos los suscritos:
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
@MessageMapping("/chat.sendMessage")
@SendTo("/topic/public")
public ChatMessage enviarMensaje(ChatMessage mensaje) {
return mensaje;
}
@MessageMapping("/chat.addUser")
@SendTo("/topic/public")
public ChatMessage agregarUsuario(ChatMessage mensaje) {
return mensaje;
}
}
Cuando un usuario entra, la app emite un evento JOIN para que todos vean quién está conectado. El flujo: el cliente envía un frame STOMP a /app/chat.sendMessage, Spring lo enruta al método, el retorno viaja a /topic/public y el broker lo reparte a cada suscriptor. Como el record es el mismo tipo en ambos sentidos, no hace falta mapeo.
El cliente STOMP en Java
Para probar el chat fuera del navegador, Spring ofrece un cliente STOMP en Java. WebSocketStompClient se construye sobre StandardWebSocketClient y se conecta al endpoint con un StompSessionHandlerAdapter:
import java.lang.reflect.Type;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
public class ChatCliente {
public static void main(String[] args) throws Exception {
WebSocketStompClient stompClient =
new WebSocketStompClient(new StandardWebSocketClient());
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
StompSession session = stompClient
.connect("ws://localhost:8080/chat",
new StompSessionHandlerAdapter() {
})
.get();
session.subscribe("/topic/public", new StompFrameHandler() {
@Override
public Type getPayloadType(StompHeaders headers) {
return ChatMessage.class;
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
System.out.println("Mensaje recibido: " + payload);
}
});
session.send("/app/chat.sendMessage",
new ChatMessage("CHAT", "ana", "Hola a todos"));
}
}
La conexión es asíncrona: connect devuelve un futuro y get() bloquea hasta que la sesión está lista. subscribe registra un StompFrameHandler que corre por cada mensaje, y send publica en el destino de la aplicación. La URL ws://localhost:8080/chat apunta al endpoint con SockJS. Este cliente tampoco compila suelto: necesita el classpath de Spring.
WebSocket vs HTTP polling vs SSE
Antes de elegir WebSocket conviene conocer las alternativas. La tabla compara los tres enfoques de comunicación en tiempo real:
| Característica | WebSocket | HTTP polling | Server-Sent Events (SSE) |
|---|---|---|---|
| Dirección | Bidireccional persistente | Petición-respuesta | Servidor a cliente |
| Latencia | Tiempo real | Intervalo fijo | Tiempo real |
| Protocolo | TCP sobre HTTP upgrade | HTTP | HTTP |
| Reconexión | Manual o con SockJS | Automática | Automática nativa |
| Uso típico | Chats, juegos, colaboración | Fallback simple | Notificaciones, feeds |
El polling abre una petición HTTP por cada comprobación, lo que dispara el tráfico al crecer el número de clientes. SSE añade streaming de servidor a cliente sobre HTTP con reconexión automática, pero es unidireccional. WebSocket es la única opción bidireccional persistente y por eso es la natural para un chat. La referencia de WebSocket de Spring detalla el modelo completo.
Cuándo elegir WebSocket y cuándo no
WebSocket resuelve un problema distinto al de Kafka con Spring Boot: Kafka mueve eventos entre sistemas con persistencia, mientras que WebSocket lleva mensajes en vivo a clientes conectados. Para una notificación puntual o una tarea en segundo plano, un endpoint REST o una cola son más simples. WebSocket brilla cuando el servidor empuja datos a muchos clientes a la vez.
La ventaja de hacerlo con Spring Boot es que la infraestructura desaparece: sesiones, broker y reconexión quedan resueltos por el framework. Para el resto del ecosistema, la serie de tutoriales de Spring Boot cubre REST, bases de datos y despliegue.
Preguntas Frecuentes
¿Qué es WebSocket?
WebSocket es un protocolo de comunicación bidireccional y persistente que se establece sobre una conexión TCP. A diferencia de HTTP, una vez abierta la conexión, tanto el cliente como el servidor pueden enviar mensajes en cualquier momento. Es la base de los chats y las aplicaciones en tiempo real.
¿Qué diferencia hay entre WebSocket y HTTP?
HTTP es un protocolo de petición-respuesta: el cliente pregunta, el servidor contesta y cada intercambio abre una comunicación nueva. WebSocket empieza como una petición HTTP con la cabecera Upgrade y después mantiene un canal abierto y bidireccional. Por eso WebSocket tiene menos sobrecarga cuando hay un intercambio continuo de mensajes.
¿Qué es STOMP y para qué sirve?
STOMP es un protocolo de mensajería simple sobre WebSocket que organiza los mensajes en destinos con prefijos. Spring lo usa para distinguir los destinos de suscripción, como /topic, de los destinos de aplicación, como /app. Aporta frames, suscripciones y una semántica común que WebSocket puro no define.
¿Qué es SockJS?
SockJS es una capa de compatibilidad que emula WebSocket cuando el navegador no lo soporta de forma nativa. Detecta el transporte disponible y elige el más adecuado, con reconexión automática. En Spring se activa simplemente llamando a withSockJS() al registrar el endpoint.
¿Spring Boot soporta WebSockets de forma nativa?
Sí. El starter spring-boot-starter-websocket incluye la API de WebSocket, el soporte STOMP y el cliente, y la anotación @EnableWebSocketMessageBroker activa todo con una configuración mínima. No necesitas servidor de aplicaciones externo: Tomcat embebido ya gestiona las conexiones.
Artículos Relacionados
Newsletter Semanal de Java
Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.
🔒 Sin spam. Cancela cuando quieras.