Spring4.0系列9-websocket簡單應用
Spring 4.0的一個最大更新是增加了websocket的支援。websocket提供了一個在web應用中的高效、雙向的通訊,需要考慮到客戶端(瀏覽器)和伺服器之間的高頻和低延時訊息交換。一般的應用場景有:線上交易、遊戲、協作、資料視覺化等。
使用websocket需要考慮的瀏覽器的支援(IE<10不支援),目前主流的瀏覽器都能很好的支援websocket。
websocket協議中有一些子協議,可以從更高的層次實現程式設計模型,就像我們使用HTTP而不是TCP一樣。這些子協議有STOMP,WAMP等。
本教程只考慮websocket的簡單實用,包含Spring對JSR-356的支援及Spring WebSocket API。
1、Java API for WebSocket(JSR-356)
Java API for WebSocket已經是Java EE 7的一部分。它定義了兩類endpoit(都是EndPoint類的子類),使用註解標識@ClientEndpoint和@ServerEndpoint。
1.1 Servlet容器掃描初始化
通過Spring初始化一個endpoint,只需配置一個SpringConfigurator在類上的@ServerEndpoint註解上。
Java程式碼- import javax.websocket.server.ServerEndpoint;
- import
- @ServerEndpoint(value = "/echo", configurator = SpringConfigurator.class);
- publicclass EchoEndpoint {
- privatefinal EchoService echoService;
- @Autowired
- public EchoEndpoint(EchoService echoService) {
- this.echoService = echoService;
- }
- @OnMessage
- publicvoid handleMessage(Session session, String message) {
- // ...
- }
- }
上例假設SpringContextLoaderListener用來載入配置,這是個典型的web應用。Servlet容器將通過掃描@ServerEndpoint和SpringConfigurator初始化一個新的websocket會話。
1.2 Spring 初始化
如果你想使用一個單獨的例項而不使用Servlet容器掃描,將EchoEndpoint類宣告稱一個bean,並增加一個ServerEndpointExporter的bean:
Java程式碼- import org.springframework.web.socket.server.endpoint.ServerEndpointExporter;
- @Configuration
- publicclass EndpointConfig {
- @Bean
- public EchoEndpoint echoEndpoint() {
- returnnew EchoEndpoint(echoService());
- }
- @Bean
- public EchoService echoService() {
- // ...
- }
- @Bean
- public ServerEndpointExporter endpointExporter() {
- returnnew ServerEndpointExporter();
- }
- }
EchoEndpoint 可以通過EndPointRegistration釋出:
Java程式碼- import org.springframework.web.socket.server.endpoint.ServerEndpointExporter;
- import org.springframework.web.socket.server.endpoint.ServerEndpointRegistration;
- @Configuration
- publicclass EndpointConfig {
- @Bean
- public EndpointRegistration echoEndpoint() {
- returnnew EndpointRegistration("/echo", EchoEndpoint.class);
- }
- @Bean
- public ServerEndpointExporter endpointExporter() {
- returnnew ServerEndpointExporter();
- }
- // ..
- }
2、Spring WebSocket API
Spring WebSocket API提供了SockJS的支援,且有些容器如Jetty 9目前還沒有對JSR-356的支援,所以有Spring WebSocket API是必要的。
Spring WebSocket API的核心介面是WebSocketHandler。下面是一個處理文字訊息的handler的實現:
Java程式碼- import org.springframework.web.socket.adapter.TextWebSocketHandlerAdapter;
- publicclass EchoHandler extends TextWebSocketHandlerAdapter {
- @Override
- publicvoid handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
- session.sendMessage(message);
- }
- }
WebSocketHandler可以通過WebSocketHttpRequestHandler插入到Spring MVC裡:
Java程式碼- import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
- @Configuration
- publicclass 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伺服器端的支援
SockJs是一個指令碼框架,它提供類似於websocket的程式設計模式但是可以適應不同的瀏覽器(包括不支援websocket的瀏覽器)。
開啟SockJS的支援,宣告一個SockJsService,和一個url對映,然後提供一個WebSocketHandler來處理訊息。雖然我們是喲個SockJS我們開發的方式是一樣的,但是隨著瀏覽器的不同傳輸的協議可以是Http Streaming,long polling等。
Java程式碼- import org.springframework.web.socket.sockjs.SockJsService;
- // ...
- @Configuration
- publicclass 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;
- }
- }
在我們實際使用中我們會使用WebSocketConfigurer集中註冊WebSocket服務:
Java程式碼- @Configuration
- @EnableWebMvc
- @EnableWebSocket//開啟websocket
- publicclass WebConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
- @Override
- publicvoid 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() {
- returnnew EchoWebSocketHandler(echoService());
- }
- @Bean
- public WebSocketHandler snakeWebSocketHandler() {
- returnnew PerConnectionWebSocketHandler(SnakeWebSocketHandler.class);
- }
- @Bean
- public DefaultEchoService echoService() {
- returnnew DefaultEchoService("Did you say \"%s\"?");
- }
- // Allow serving HTML files through the default Servlet
- @Override
- publicvoid configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
- configurer.enable();
- }
- }
SockJS客戶端程式碼:
Java程式碼- 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);//向服務端傳送訊息
程式用maven打成war後用tomcat 8釋出檢視效果。
新書推薦《JavaEE開發的顛覆者: Spring Boot實戰》,涵蓋Spring 4.x、Spring MVC 4.x、Spring Boot企業開發實戰。
或自己在京東、淘寶、亞馬遜、噹噹、互動出版社搜尋自選。