Spring4.0.6 Websocket详细配置 之 消息模块

===============================================

环境介绍:

Jdk 1.7

Tomcat7.0.52 (支持Websocket协议)

Spring4.0.26 (支持Websocket)

web.xml(配置了前端自动优化HtmlCompressor和Druid监控),自动优化会影响Websocket js脚本,后面会讲

=================================================

配置步骤:

1. 引入Spring相关Jar,特别需要下面这两个

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>4.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>4.0.6.RELEASE</version>
        </dependency>

 

2. 编写WebSocketConfig implements WebSocketConfigurer

WebSocketConfig.java

 

Java代码   收藏代码
  1. import org.springframework.context.annotation.Bean;  
  2. import org.springframework.context.annotation.Configuration;  
  3. import org.springframework.web.servlet.config.annotation.EnableWebMvc;  
  4. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;  
  5. import org.springframework.web.socket.config.annotation.EnableWebSocket;  
  6. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;  
  7. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;  
  8. import org.springframework.web.socket.handler.TextWebSocketHandler;  
  9.   
  10. import cn.com.ship.message.handler.ChatMessageHandler;  
  11. import cn.com.ship.message.handler.TextMessageHandler;  
  12.   
  13. @Configuration  
  14. <strong><span style="color: #ff0000;">//@EnableWebMvc//这个标注可以不加,如果有加,要</span></strong><strong><span style="color: #ff0000;"><strong><span style="color: #ff0000;">extends WebMvcConfigurerAdapter</span></strong></span></strong>  
  15. @EnableWebSocket  
  16. public class WebSocketConfig implements WebSocketConfigurer {  
  17.     @Override  
  18.     public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {  
  19.         registry.addHandler(chatMessageHandler(),"/websocket/chatMessageServer.do").addInterceptors(new ChatHandshakeInterceptor());  
  20.         registry.addHandler(chatMessageHandler(), "/sockjs/chatMessageServer.do").addInterceptors(new ChatHandshakeInterceptor()).withSockJS();  
  21.     }  
  22.    
  23.     @Bean  
  24.     public TextWebSocketHandler chatMessageHandler(){  
  25.         return new ChatMessageHandler();  
  26.     }  
  27.   
  28. }  

 3. 编写ChatMessageHandler extends TextWebSocketHandler

ChatMessageHandler.java

 

 

Java代码   收藏代码
  1. import java.io.IOException;  
  2. import java.util.ArrayList;  
  3.   
  4. import org.apache.log4j.Logger;  
  5. import org.springframework.web.socket.CloseStatus;  
  6. import org.springframework.web.socket.TextMessage;  
  7. import org.springframework.web.socket.WebSocketSession;  
  8. import org.springframework.web.socket.handler.TextWebSocketHandler;  
  9.   
  10. import cn.com.ship.message.common.Constants;  
  11. import cn.com.ship.message.common.MessageCriteria;  
  12.   
  13. public class ChatMessageHandler extends TextWebSocketHandler{  
  14.   
  15.     private static final ArrayList<WebSocketSession> users;//这个会出现性能问题,最好用Map来存储,key用userid  
  16.     private static Logger logger = Logger.getLogger(ChatMessageHandler.class);  
  17.     static {  
  18.         users = new ArrayList<WebSocketSession>();  
  19.     }  
  20.       
  21.     public ChatMessageHandler() {  
  22.         // TODO Auto-generated constructor stub  
  23.     }  
  24.           
  25.     /** 
  26.      * 连接成功时候,会触发UI上onopen方法 
  27.      */  
  28.     @Override  
  29.     public void afterConnectionEstablished(WebSocketSession session) throws Exception {  
  30.         System.out.println("connect to the websocket success......");  
  31.     users.add(session);  
  32.     //这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户  
  33.         //TextMessage returnMessage = new TextMessage("你将收到的离线");  
  34.     //session.sendMessage(returnMessage);  
  35.     }  
  36.   
  37.     /** 
  38.      * 在UI在用js调用websocket.send()时候,会调用该方法 
  39.      */  
  40.     @Override  
  41.     protected void handleTextMessage(WebSocketSession session,  
  42.             TextMessage message) throws Exception {  
  43.         super.handleTextMessage(session, message);  
  44.                   
  45.     }  
  46.   
  47.     /** 
  48.      * 给某个用户发送消息 
  49.      * 
  50.      * @param userName 
  51.      * @param message 
  52.      */  
  53.     public void sendMessageToUser(String userName, TextMessage message) {  
  54.         for (WebSocketSession user : users) {  
  55.             if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {  
  56.                 try {  
  57.                     if (user.isOpen()) {  
  58.                         user.sendMessage(message);  
  59.                     }  
  60.                 } catch (IOException e) {  
  61.                     e.printStackTrace();  
  62.                 }  
  63.                 break;  
  64.             }  
  65.         }  
  66.     }  
  67.       
  68.     /** 
  69.      * 给所有在线用户发送消息 
  70.      * 
  71.      * @param message 
  72.      */  
  73.     public void sendMessageToUsers(TextMessage message) {  
  74.         for (WebSocketSession user : users) {  
  75.             try {  
  76.                 if (user.isOpen()) {  
  77.                     user.sendMessage(message);  
  78.                 }  
  79.             } catch (IOException e) {  
  80.                 e.printStackTrace();  
  81.             }  
  82.         }  
  83.     }  
  84.       
  85.     @Override  
  86.     public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {  
  87.         if(session.isOpen()){  
  88.             session.close();  
  89.         }  
  90.         logger.debug("websocket connection closed......");  
  91.         users.remove(session);  
  92.     }  
  93.       
  94.     @Override  
  95.     public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {  
  96.         logger.debug("websocket connection closed......");  
  97.         users.remove(session);  
  98.     }  
  99.       
  100.     @Override  
  101.     public boolean supportsPartialMessages() {  
  102.         return false;  
  103.     }  
  104.   
  105. }  

 

 

4. 编写websocket握手拦截器ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor

ChatHandshakeInterceptor.java

 

Java代码   收藏代码
  1. package cn.com.ship.message.websocket;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import javax.servlet.http.HttpSession;  
  6.   
  7. import org.springframework.http.server.ServerHttpRequest;  
  8. import org.springframework.http.server.ServerHttpResponse;  
  9. import org.springframework.http.server.ServletServerHttpRequest;  
  10. import org.springframework.web.socket.WebSocketHandler;  
  11. import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  
  12.   
  13. import cn.com.ship.message.common.Constants;  
  14.   
  15. public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {  
  16.   
  17.     @Override  
  18.     public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {  
  19.         System.out.println("Before Handshake");  
  20.         if (request instanceof ServletServerHttpRequest) {  
  21.             ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;  
  22.             HttpSession session = servletRequest.getServletRequest().getSession(false);  
  23.             if (session != null) {  
  24.                 //使用userName区分WebSocketHandler,以便定向发送消息  
  25.                 String userName = (String) session.getAttribute(Constants.SESSION_USERNAME);  
  26.                 if (userName==null) {  
  27.                     userName="default-system";  
  28.                 }  
  29.                 attributes.put(Constants.WEBSOCKET_USERNAME,userName);  
  30.                   
  31.             }  
  32.         }  
  33.         return super.beforeHandshake(request, response, wsHandler, attributes);  
  34.     }  
  35.   
  36.     @Override  
  37.     public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {  
  38.         System.out.println("After Handshake");  
  39.         super.afterHandshake(request, response, wsHandler, ex);  
  40.     }  
  41.       
  42.   
  43. }  

 

 

4. 重点在Spring mvc相关配置(经常出现问题就是:中文乱码,如果是用ajax交互)

 

Xml代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"  
  4.     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"  
  5.     xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"  
  6.     xmlns:tool="http://www.springframework.org/schema/tool" xmlns:context="http://www.springframework.org/schema/context"  
  7.     xmlns:task="http://www.springframework.org/schema/task"  
  8.     xmlns:mvc="http://www.springframework.org/schema/mvc"  
  9.     xmlns:websocket="http://www.springframework.org/schema/websocket"  
  10.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  11.     http://www.springframework.org/schema/beans/spring-beans.xsd    
  12.     http://www.springframework.org/schema/tx    
  13.     http://www.springframework.org/schema/tx/spring-tx.xsd    
  14.     http://www.springframework.org/schema/aop    
  15.     http://www.springframework.org/schema/aop/spring-aop.xsd    
  16.     http://www.springframework.org/schema/jee    
  17.     http://www.springframework.org/schema/jee/spring-jee.xsd    
  18.     http://www.springframework.org/schema/context    
  19.     http://www.springframework.org/schema/context/spring-context.xsd    
  20.     http://www.springframework.org/schema/util    
  21.     http://www.springframework.org/schema/util/spring-util.xsd    
  22.     http://www.springframework.org/schema/tool    
  23.     http://www.springframework.org/schema/tool/spring-tool.xsd  
  24.     http://www.springframework.org/schema/task   
  25.     http://www.springframework.org/schema/task/spring-task.xsd  
  26.     http://www.springframework.org/schema/websocket   
  27.     http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd  
  28.     http://www.springframework.org/schema/mvc   
  29.     http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">  
  30.       
  31.     <!-- 这个bean要放在context:component-scan这个前面,不然会出现中文乱码 -->  
  32.     <bean  
  33.         class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">  
  34.         <property name="messageConverters">  
  35.             <list>  
  36.                 <ref bean="stringHttpMessageConverter" />  
  37.                 <ref bean="byteArrayHttpMessageConverter" />  
  38.                 <ref bean="jsonHttpMessageConverter" />  
  39.                 <ref bean="jsonHttpMessageConverter4JS" />  
  40.             </list>  
  41.         </property>  
  42.     </bean>  
  43.       
  44.     <!-- 启动SpringMVC Controller的注解功能,完成请求和注解POJO的映射 -->  
  45.     <context:component-scan base-package="cn.com.ship.*.**.controller" />  
  46.       
  47.     <!-- websocket相关扫描,主要扫描:WebSocketConfig.java 这个类路径 -->  
  48.     <context:component-scan base-package="cn.com.ship.message.websocket"/>  
  49.       
  50.     <!-- 下面标签可以不加 等价于所有component-scan-->  
  51.     <context:annotation-config />  
  52.       
  53.     <!-- 这个重点,标注必须加,websocket用到-->  
  54.     <mvc:annotation-driven/>  
  55.       
  56.     <bean id="byteArrayHttpMessageConverter"  
  57.         class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />  
  58.           
  59.     <bean id="stringHttpMessageConverter"  
  60.         class="org.springframework.http.converter.StringHttpMessageConverter">  
  61.         <property name="supportedMediaTypes">  
  62.             <list>  
  63.                 <value>text/plain;charset=UTF-8</value>  
  64.             </list>  
  65.         </property>  
  66.     </bean>  
  67.       
  68.     <bean id="jsonHttpMessageConverter"  
  69.         class="cn.com.ship.external.spring.converter.json.MappingJackson2HttpMessageConverter">  
  70.         <property name="objectMapper">  
  71.             <bean class="com.fasterxml.jackson.databind.ObjectMapper">  
  72.                 <!--对属性值为null的不序列化反序列化-->  
  73.                 <property name="serializationInclusion">  
  74.                     <util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL"/>  
  75.                 </property>  
  76.             </bean>  
  77.         </property>  
  78.         <property name="supportedMediaTypes">  
  79.             <list>  
  80.                 <value>application/json</value>  
  81.             </list>  
  82.         </property>  
  83.     </bean>  
  84.       
  85.     <bean id="jsonHttpMessageConverter4JS"  
  86.         class="cn.com.ship.external.spring.converter.json.MappingJackson2HttpMessageConverter">  
  87.         <property name="objectMapper">  
  88.             <bean class="com.fasterxml.jackson.databind.ObjectMapper">  
  89.                 <property name="dateFormat">  
  90.                     <bean class="java.text.SimpleDateFormat">  
  91.                         <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />  
  92.                     </bean>  
  93.                 </property>  
  94.                 <!--对属性值为null的不序列化反序列化-->  
  95.                 <property name="serializationInclusion">  
  96.                     <util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL"/>  
  97.                 </property>  
  98.             </bean>  
  99.         </property>  
  100.         <property name="supportedMediaTypes">  
  101.             <list>  
  102.                 <value>text/json</value>  
  103.             </list>  
  104.         </property>  
  105.     </bean>  
  106.       
  107.     <bean id="viewResolver"  
  108.         class="org.springframework.web.servlet.view.UrlBasedViewResolver">  
  109.         <property name="viewClass"  
  110.             value="org.springframework.web.servlet.view.JstlView" />  
  111.         <property name="prefix" value="/" />  
  112.         <property name="suffix" value=".jsp" />  
  113.     </bean>  
  114.   
  115.     <!-- 文件上传配置 -->  
  116.     <bean id="multipartResolver"  
  117.         class="org.springframework.web.multipart.commons.CommonsMultipartResolver"  
  118.         p:defaultEncoding="utf-8">  
  119.         <property name="maxUploadSize" value="1024000000" />  
  120.         <property name="resolveLazily" value="true" />  
  121.     </bean>  
  122. </beans>  

 注意:MappingJackson2HttpMessageConverter.java,来自Spring代码,并且修改了一点点,这个找到附件位置下载

 

 

5. jsp相关Websocket脚本编写

     ws.jsp

 

Java代码   收藏代码
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  2.   
  3. <html>  
  4. <head>  
  5.     <title>Java API for WebSocket (JSR-356)</title>    
  6. </head>  
  7. <body>  
  8. <script type="text/javascript" src="http://localhost:8080/ship/js/jquery/jquery.min.js"></script>  
  9. <script type="text/javascript" src="http://localhost:8080/ship/js/sockjs-0.3.4.min.js"></script>  
  10. <script type="text/javascript">  
  11.     var websocket = null;  
  12.     if ('WebSocket' in window) {  
  13.         websocket = new WebSocket("ws://localhost:8080/ship/webSocketServer.do");  
  14.     }   
  15.     else if ('MozWebSocket' in window) {  
  16.         websocket = new MozWebSocket("ws://localhost:8080/ship/webSocketServer.do");  
  17.     }   
  18.     else {  
  19.         websocket = new SockJS("http://localhost:8080/ship/sockjs/webSocketServer.do");  
  20.     }  
  21.     websocket.onopen = onOpen;  
  22.     websocket.onmessage = onMessage;  
  23.     websocket.onerror = onError;  
  24.     websocket.onclose = onClose;  
  25.               
  26.     function onOpen(openEvt) {  
  27.         //alert(openEvt.Data);  
  28.     }  
  29.       
  30.     function onMessage(evt) {  
  31.         alert(evt.data);  
  32.     }  
  33.     function onError() {}  
  34.     function onClose() {}  
  35.       
  36.     function doSend() {  
  37.         if (websocket.readyState == websocket.OPEN) {         
  38.             var msg = document.getElementById("inputMsg").value;    
  39.             websocket.send(msg);//调用后台handleTextMessage方法  
  40.             alert("发送成功!");    
  41.         } else {    
  42.             alert("连接失败!");    
  43.         }    
  44.     }  
  45. </script>  
  46. 请输入:<textarea rows="5" cols="10" id="inputMsg" name="inputMsg"></textarea>  
  47. <button onclick="doSend();">发送</button>  
  48. </body>  
  49. </html>  

    login.jsp

 

 

Java代码   收藏代码
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"  
  2.     pageEncoding="UTF-8"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  4. <html>  
  5. <head>  
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  7. <title>Java API for WebSocket (JSR-356)</title>   
  8. </head>  
  9. <body>  
  10.         <!-ship是我的项目名-->  
  11.     <form action="/ship/websocket/login.do">  
  12.         登录名:<input type="text" name="username"/>  
  13.         <input type="submit" value="登录"/>  
  14.     </form>  
  15.   
  16. </body>  
  17. </html>  

 

 

5. 调用端Controller编写 WebsocketController.java

 

Java代码   收藏代码
  1. package cn.com.ship.websocket.controller;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import javax.servlet.http.HttpServletResponse;  
  5. import javax.servlet.http.HttpSession;  
  6.   
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8. import org.springframework.stereotype.Controller;  
  9. import org.springframework.web.bind.annotation.RequestMapping;  
  10. import org.springframework.web.bind.annotation.ResponseBody;  
  11.   
  12. @Controller  
  13. public class WebsocketController {  
  14.   
  15.     @Bean//这个注解会从Spring容器拿出Bean  
  16.     public InfoHandler infoHandler() {  
  17.         return new InfoHandler();  
  18.     }  
  19.       
  20.     @RequestMapping("/websocket/login")  
  21.     public void login(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  22.         String username = request.getParameter("username");  
  23.         HttpSession session = request.getSession(false);  
  24.         session.setAttribute(Constants.SESSION_USERNAME, username);  
  25.           
  26.         response.sendRedirect("/ship/websocket/ws.jsp");  
  27.     }  
  28.   
  29.     @RequestMapping("/websocket/send")  
  30.     @ResponseBody  
  31.     public String send(HttpServletRequest request) {  
  32.           
  33.         String username = request.getParameter("username");  
  34.         infoHandler().sendMessageToUser(username, new TextMessage("你好,测试!!!!"));  
  35.           
  36.         return null;  
  37.     }  
  38.   
  39. }  

 

 

6. 测试Websocket是否连接成功

   先在login.jsp上随便输入用户名,然后WebsocketController处理完请求,会重定向到ws.jsp

   如果后台打印出现消息,证明Websocket连接成功(前两名消息是在ChatHandshakeInterceptor.java,最后一句是在ChatMessageHandler.java)

Before Handshake
After Handshake
connect to the websocket success......

猜你喜欢

转载自aoyouzi.iteye.com/blog/2310350