Primavera Framework proporciona una API para el protocolo WebSocket que puede utilizar para escribir aplicaciones de cliente y servidor que manejen mensajes WebSocket.
WebSocketHandler
Cree un servidor WebSocket el De la misma manera, simplemente cómo implementar WebSocketHandler
o, más probablemente, extender TextWebSocketHandler
o BinaryWebSocketHandler
. El siguiente ejemplo utiliza TextWebSocketHandler
:
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
public class MyHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
}
}
Hay un Java especial configuración Soporte de espacio de nombres XML y WebSocket para asignar un controlador WebSocket anterior a una URL específica, como se muestra en el siguiente ejemplo:
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
El siguiente ejemplo muestra el equivalente XML de la configuración del ejemplo anterior:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
El ejemplo anterior está pensado para su uso en aplicaciones Spring MVC y debe incluirse en la configuración de DispatcherServlet
. Sin embargo, el soporte de Spring WebSocket es independiente de Spring MVC. Con WebSocketHandler
es relativamente fácil integrar WebSocketHandler
en otros marcos HTTP usando WebSocketHttpRequestHandler
.
Cuándo Al utilizar la API WebSocketHandler
directa o indirectamente, como a través de la mensajería STOMP, la aplicación debe sincronizar el envío de mensajes porque la sesión WebSocket estándar subyacente (JSR-356) no permite el envío simultáneo. Una opción es empaquetar WebSocketSession
con ConcurrentWebSocketSessionDecorator
.
Apretón de enlace de WebSocket
La forma más sencilla de configurar el protocolo de enlace HTTP inicial La solicitud para WebSocket es un HandshakeInterceptor
que expone los métodos de protocolo de enlace "antes" y "después". Puede utilizar dicho interceptor para evitar el protocolo de enlace o exponer algunos atributos a WebSocketSession
. El siguiente ejemplo utiliza un interceptor integrado para pasar atributos de sesión HTTP a una sesión WebSocket:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/myHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
El siguiente ejemplo muestra el equivalente XML de la configuración del ejemplo anterior:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
Una opción más avanzada es la extensión DefaultHandshakeHandler
, que realiza todos los pasos del protocolo de enlace para un WebSocket, incluido comprobar origen de cliente, aprobación de subprotocolo, etc. Es posible que una aplicación también necesite usar esta opción si necesita configurar una RequestUpgradeStrategy
personalizada para adaptarse a una versión del motor y del servidor WebSocket que aún no es compatible. Tanto la configuración de Java como el espacio de nombres XML le permiten configurar un HandshakeHandler
personalizado.
WebSocketHandlerDecorator
, que se puede utilizar para decorar
WebSocketHandler
con lógica operativa adicional. Las implementaciones de registro y manejo de excepciones se proporcionan y agregan de forma predeterminada cuando se utiliza una configuración Java WebSocket o un espacio de nombres XML.
ExceptionWebSocketHandlerDecorator
detecta todas las excepciones no detectadas generadas por cualquier método
WebSocketHandler
y cierra la sesión de WebSocket con el estado
1011
, lo que indica un error del servidor.
Implementación
La API Spring WebSocket es fácil de integrar en una aplicación Spring MVC si el DispatcherServlet
maneja tanto el protocolo de enlace HTTP de WebSocket como otras solicitudes HTTP. También se puede integrar fácilmente en otros scripts de procesamiento HTTP llamando a WebSocketHttpRequestHandler
. Es conveniente y comprensible. Sin embargo, existen consideraciones especiales para los tiempos de ejecución JSR-356.
La API Java WebSocket (JSR-356) proporciona dos mecanismos de implementación. El primero implica escanear el classpath del contenedor de servlet (función Servlet 3) al inicio. La otra es una API de registro que se utiliza al inicializar un contenedor de servlets. Ninguno de estos mecanismos permite que se utilice un único "controlador frontal" para todo el procesamiento HTTP, incluido el protocolo de enlace WebSocket y todas las demás solicitudes HTTP, como DispatcherServlet
en Spring MVC.
Esto Una limitación importante de JSR-356 es que el soporte WebSocket de Spring aborda implementaciones RequestUpgradeStrategy
específicas del servidor, incluso cuando se ejecuta en el tiempo de ejecución de JSR-356. Actualmente existen estrategias de este tipo para Tomcat, Jetty, GlassFish, WebLogic, WebSphere y Undertow (y WildFly).
Un punto secundario es que los contenedores de servlets habilitados para JSR-356 deben realizar un análisis ServletContainerInitializer
(SCI), lo que puede ralentizar el inicio. de la solicitud, en algunos casos de forma significativa. Si experimenta una desaceleración significativa después de actualizar a una versión habilitada para JSR-356 del contenedor de servlets, debería poder habilitar o deshabilitar selectivamente los fragmentos web (y el escaneo SCI) usando Elemento <absolute-ordering/>
en web.xml
, como se muestra en el siguiente ejemplo:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<absolute-ordering/>
</web-app>
Luego puede activar selectivamente fragmentos web por nombre, por ejemplo el propio SpringServletContainerInitializer
de Spring, que proporciona soporte para la API de inicialización de Java para Servlet 3 El siguiente ejemplo muestra cómo hacer esto:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<absolute-ordering>
<name>spring_web</name>
</absolute-ordering>
</web-app>
Configuración del servidor
Cada mecanismo del protocolo WebSocket subyacente expone propiedades de configuración que controlan las características del tiempo de ejecución, como tamaño del búfer de mensajes, tiempo de espera de inactividad y otros.
Para Tomcat, WildFly y GlassFish, puede agregar ServletServerContainerFactoryBean
a la configuración de WebSocket Java, como se muestra en el siguiente ejemplo:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}
El siguiente ejemplo muestra el equivalente XML de la configuración del ejemplo anterior:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<bean class="org.springframework...ServletServerContainerFactoryBean">
<property name="maxTextMessageBufferSize" value="8192"/>
<property name="maxBinaryMessageBufferSize" value="8192"/>
</bean>
</beans>
WebSocketContainerFactoryBean
(XML) o
ContainerProvider.getWebSocketContainer()
(configuración de Java).
En el caso de Jetty, debe proporcionar un preconfigurado WebSocketServerFactory
para Jetty y conéctelo a DefaultHandshakeHandler
desde Spring a través de la configuración de Java para WebSocket. El siguiente ejemplo muestra cómo hacer esto:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(),
"/echo").setHandshakeHandler(handshakeHandler());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(
new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
}
}
El siguiente ejemplo muestra el equivalente XML de la configuración del ejemplo anterior:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/echo" handler="echoHandler"/>
<websocket:handshake-handler ref="handshakeHandler"/>
</websocket:handlers>
<bean id="handshakeHandler" class="org.springframework...DefaultHandshakeHandler">
<constructor-arg ref="upgradeStrategy"/>
</bean>
<bean id="upgradeStrategy" class="org.springframework...JettyRequestUpgradeStrategy">
<constructor-arg ref="serverFactory"/>
</bean>
<bean id="serverFactory" class="org.eclipse.jetty...WebSocketServerFactory">
<constructor-arg>
<bean class="org.eclipse.jetty...WebSocketPolicy">
<constructor-arg value="SERVER"/>
<property name="inputBufferSize" value="8092"/>
<property name="idleTimeout" value="600000"/>
</bean>
</constructor-arg>
</bean>
</beans>
Fuentes aceptables
A partir de Spring Framework 4.1.5, la lógica predeterminada para WebSocket y SockJS es solo aceptar solicitudes de la misma fuente. También puede permitir todas o una lista específica de fuentes. Esta verificación está destinada principalmente a clientes de navegador. No hay nada que impida que otros tipos de clientes cambien el valor del encabezado Origin
(consulte " para más detalles RFC 6454: Concepto de origen web).
Las siguientes tres opciones de lógica operativa son posibles:
Permitir solo solicitudes del mismo origen (predeterminado): en este modo, si SockJS está habilitado, el marco en línea de respuesta HTTP (Iframe) para el encabezado
X-Frame-Options
se establece enSAMEORIGIN
y el mecanismo de paso JSONP está deshabilitado porque no le permite verificar el origen de la solicitud. Como resultado, no hay soporte para IE6 e IE7 si este modo está habilitado.Permitir una lista especificada de fuentes: cada fuente válida debe comenzar con
http://
ohttps://
. En este modo, si SockJS está activado, el paso de iframe está deshabilitado. Como resultado, no hay soporte para IE6 a IE9 si este modo está habilitado.Permitir cualquier fuente: para habilitar este modo, debe especificar
*
como valores fuente válidos. En este modo, todos los mecanismos de transmisión están disponibles.
Puedes configurar fuentes WebSocket y SockJS válidas, como se muestra en el siguiente ejemplo:
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
El siguiente ejemplo muestra el equivalente XML de la configuración del ejemplo anterior:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="https://mydomain.com">
<websocket:mapping path="/myHandler" handler="myHandler" />
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
GO TO FULL VERSION