One of the biggest updates in Spring 4.0 is the addition of websocket support. Websockets provide an efficient, two-way communication in web applications, taking into account the high-frequency and low-latency message exchange between the client (browser) and the server. General application scenarios include: online transactions, games, collaboration, data visualization, etc.
Browser support needs to be considered when using websocket (IE<10 does not support it). Currently, mainstream browsers can support websocket very well.
There are some sub-protocols in the websocket protocol, which can realize the programming model from a higher level, just like we use HTTP instead of TCP. These sub-protocols include STOMP, WAMP, etc.
This tutorial only considers the simplicity and practicality of websocket, including Spring's support for JSR-356 and Spring WebSocket API.
1、Java API for WebSocket(JSR-356)
The Java API for WebSocket is already part of Java EE 7. It defines two types of endpoints (both subclasses of the EndPoint class), using annotations to identify @ClientEndpoint and @ServerEndpoint.
1.1 Servlet container scan initialization
To initialize an endpoint through Spring, just configure a SpringConfigurator on the @ServerEndpoint annotation on the class.
import javax.websocket.server.ServerEndpoint;
import org.springframework.web.socket.server.endpoint.SpringConfigurator;
@ServerEndpoint(value = "/echo", configurator = SpringConfigurator.class);
public class EchoEndpoint {
private final EchoService echoService;
@Autowired
public EchoEndpoint(EchoService echoService) {
this.echoService = echoService;
}
@OnMessage
public void handleMessage(Session session, String message) {
// ...
}
}
The above example assumes that SpringContextLoaderListener is used to load configuration, which is a typical web application. The Servlet container will initialize a new websocket session by scanning @ServerEndpoint and SpringConfigurator.
1.2 Spring initialization
If you want to use a single instance without Servlet container scanning, declare the EchoEndpoint class as a bean and add a ServerEndpointExporter bean:
import org.springframework.web.socket.server.endpoint.ServerEndpointExporter;
@Configuration
public class EndpointConfig {
@Bean
public EchoEndpoint echoEndpoint() {
return new EchoEndpoint(echoService());
}
@Bean
public EchoService echoService() {
// ...
}
@Bean
public ServerEndpointExporter endpointExporter() {
return new ServerEndpointExporter();
}
}
EchoEndpoint can be published via EndPointRegistration:
import org.springframework.web.socket.server.endpoint.ServerEndpointExporter;
import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
@Configuration
public class EndpointConfig {
@Bean
public EndpointRegistration echoEndpoint() {
return new EndpointRegistration("/echo", EchoEndpoint.class);
}
@Bean
public ServerEndpointExporter endpointExporter() {
return new ServerEndpointExporter();
}
// ..
}
The source code of this example: spring-websocket-test-endpoint.zip
2、Spring WebSocket API
Spring WebSocket API provides support for SockJS, and some containers such as Jetty 9 currently do not support JSR-356, so it is necessary to have Spring WebSocket API.
The core interface of Spring WebSocket API is WebSocketHandler. The following is an implementation of a handler that handles text messages:
import org.springframework.web.socket.adapter.TextWebSocketHandlerAdapter;
public class EchoHandler extends TextWebSocketHandlerAdapter {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
session.sendMessage(message);
}
}
WebSocketHandler can be inserted into Spring MVC via WebSocketHttpRequestHandler:
import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
@Configuration
public class WebConfig {
@Bean
public SimpleUrlHandlerMapping handlerMapping() {
Map<String, Object> urlMap = new HashMap<String, Object>();
urlMap.put("/echo", new WebSocketHttpRequestHandler(new EchoHandler()));
SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping();
hm.setUrlMap(urlMap);
return hm;
}
}
SockJS server-side support
SockJs is a scripting framework that provides a programming mode similar to websocket but can be adapted to different browsers (including browsers that do not support websocket).
Enable SockJS support, declare a SockJsService, and a url mapping, and then provide a WebSocketHandler to process messages. Although we are a SockJS, our development method is the same, but the transmission protocol can be Http Streaming, long polling, etc. depending on the browser.
import org.springframework.web.socket.sockjs.SockJsService;
// ...
@Configuration
public class WebConfig {
@Bean
public SimpleUrlHandlerMapping handlerMapping() {
SockJsService sockJsService = new DefaultSockJsService(taskScheduler());
Map<String, Object> urlMap = new HashMap<String, Object>();
urlMap.put("/echo/**", new SockJsHttpRequestHandler(sockJsService, new EchoHandler()));
SimpleUrlHandlerMapping hm = new SimpleUrlHandlerMapping();
hm.setUrlMap(urlMap);
return hm;
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setThreadNamePrefix("SockJS-");
return taskScheduler;
}
}
In our actual use, we will use WebSocketConfigurer to centrally register WebSocket services:
@Configuration
@EnableWebMvc
@EnableWebSocket//开启websocket
public class WebConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo"); //提供符合W3C标准的Websocket数据
registry.addHandler(snakeWebSocketHandler(), "/snake");
registry.addHandler(echoWebSocketHandler(), "/sockjs/echo").withSockJS();//提供符合SockJS的数据
registry.addHandler(snakeWebSocketHandler(), "/sockjs/snake").withSockJS();
}
@Bean
public WebSocketHandler echoWebSocketHandler() {
return new EchoWebSocketHandler(echoService());
}
@Bean
public WebSocketHandler snakeWebSocketHandler() {
return new PerConnectionWebSocketHandler(SnakeWebSocketHandler.class);
}
@Bean
public DefaultEchoService echoService() {
return new DefaultEchoService("Did you say \"%s\"?");
}
// Allow serving HTML files through the default Servlet
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
SockJS client code:
ws = new SockJS(url, undefined, {protocols_whitelist: transports}) ; //初始化 websocket
ws.onopen = function () {
setConnected(true);
log('Info: connection opened.');
};
ws.onmessage = function (event) {
log('Received: ' + event.data); //处理服务端返回消息
};
ws.onclose = function (event) {
setConnected(false);
log('Info: connection closed.');
log(event);
};
ws.send(message);//向服务端发送消息
After the program is made into war with maven, it is published with tomcat 8 to view the effect.
Source code of this example: spring-websocket-test-master.zip
Original article address: http://wiselyman.iteye.com/blog/2003336