Spring4.0.6 Websocket详细配置

来源:互联网 发布:鲁南制药知乎 编辑:程序博客网 时间:2024/05/02 01:04

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

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

历经一周把整个消息模块开发完毕,其中在websocket这块遇到比较多的问题是中文乱码,因为项目中用ajax跟后端交互,用@Response注解时候出现中文乱码,需要Spring MVC相关配置,这块遇到配置会在另一个文章体现。

鉴于网上提供的一些文章,都介绍不是很到位,关键部分都没体现,导致在真实项目中出现各种各样的问题。

 

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

环境介绍:

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

 

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;import org.springframework.web.socket.handler.TextWebSocketHandler;import cn.com.ship.message.handler.ChatMessageHandler;import cn.com.ship.message.handler.TextMessageHandler;@Configuration<strong><span style="color:#ff0000;">//@EnableWebMvc//这个标注可以不加,如果有加,要</span><span style="color:#ff0000;"><strong><span style="color:#ff0000;">extends WebMvcConfigurerAdapter</span></strong></span></strong>@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer {    @Override    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {        registry.addHandler(chatMessageHandler(),"/websocket/chatMessageServer.do").addInterceptors(new ChatHandshakeInterceptor());        registry.addHandler(chatMessageHandler(), "/sockjs/chatMessageServer.do").addInterceptors(new ChatHandshakeInterceptor()).withSockJS();    }     @Bean    public TextWebSocketHandler chatMessageHandler(){        return new ChatMessageHandler();    }}

 3. 编写ChatMessageHandler extends TextWebSocketHandler

ChatMessageHandler.java

 

import java.io.IOException;import java.util.ArrayList;import org.apache.log4j.Logger;import org.springframework.web.socket.CloseStatus;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.handler.TextWebSocketHandler;import cn.com.ship.message.common.Constants;import cn.com.ship.message.common.MessageCriteria;public class ChatMessageHandler extends TextWebSocketHandler{private static final ArrayList<WebSocketSession> users;//这个会出现性能问题,最好用Map来存储,key用useridprivate static Logger logger = Logger.getLogger(ChatMessageHandler.class);static {users = new ArrayList<WebSocketSession>();}public ChatMessageHandler() {// TODO Auto-generated constructor stub}/** * 连接成功时候,会触发UI上onopen方法 */@Override    public void afterConnectionEstablished(WebSocketSession session) throws Exception {System.out.println("connect to the websocket success......");users.add(session);//这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户        //TextMessage returnMessage = new TextMessage("你将收到的离线");//session.sendMessage(returnMessage);    }/** * 在UI在用js调用websocket.send()时候,会调用该方法 */@Overrideprotected void handleTextMessage(WebSocketSession session,TextMessage message) throws Exception {super.handleTextMessage(session, message);}/**     * 给某个用户发送消息     *     * @param userName     * @param message     */    public void sendMessageToUser(String userName, TextMessage message) {        for (WebSocketSession user : users) {            if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {                try {                    if (user.isOpen()) {                        user.sendMessage(message);                    }                } catch (IOException e) {                    e.printStackTrace();                }                break;            }        }    }        /**     * 给所有在线用户发送消息     *     * @param message     */    public void sendMessageToUsers(TextMessage message) {        for (WebSocketSession user : users) {            try {                if (user.isOpen()) {                    user.sendMessage(message);                }            } catch (IOException e) {                e.printStackTrace();            }        }    }        @Override    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {        if(session.isOpen()){            session.close();        }        logger.debug("websocket connection closed......");        users.remove(session);    }        @Override    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {        logger.debug("websocket connection closed......");        users.remove(session);    }        @Override    public boolean supportsPartialMessages() {        return false;    }}

 

 

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

ChatHandshakeInterceptor.java

 

package cn.com.ship.message.websocket;import java.util.Map;import javax.servlet.http.HttpSession;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.http.server.ServletServerHttpRequest;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;import cn.com.ship.message.common.Constants;public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {System.out.println("Before Handshake");if (request instanceof ServletServerHttpRequest) {            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;            HttpSession session = servletRequest.getServletRequest().getSession(false);            if (session != null) {                //使用userName区分WebSocketHandler,以便定向发送消息                String userName = (String) session.getAttribute(Constants.SESSION_USERNAME);                if (userName==null) {                userName="default-system";                }                attributes.put(Constants.WEBSOCKET_USERNAME,userName);                            }        }        return super.beforeHandshake(request, response, wsHandler, attributes);}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {System.out.println("After Handshake");super.afterHandshake(request, response, wsHandler, ex);}}

 

 

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

 

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"xmlns:tool="http://www.springframework.org/schema/tool" xmlns:context="http://www.springframework.org/schema/context"xmlns:task="http://www.springframework.org/schema/task"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:websocket="http://www.springframework.org/schema/websocket"xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd  http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx.xsd  http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop.xsd  http://www.springframework.org/schema/jee  http://www.springframework.org/schema/jee/spring-jee.xsd  http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd  http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util.xsd  http://www.springframework.org/schema/tool  http://www.springframework.org/schema/tool/spring-tool.xsdhttp://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsdhttp://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"><!-- 这个bean要放在context:component-scan这个前面,不然会出现中文乱码 --><beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"><property name="messageConverters"><list><ref bean="stringHttpMessageConverter" /><ref bean="byteArrayHttpMessageConverter" /><ref bean="jsonHttpMessageConverter" /><ref bean="jsonHttpMessageConverter4JS" /></list></property></bean><!-- 启动SpringMVC Controller的注解功能,完成请求和注解POJO的映射 --><context:component-scan base-package="cn.com.ship.*.**.controller" /><!-- websocket相关扫描,主要扫描:WebSocketConfig.java 这个类路径 --><context:component-scan base-package="cn.com.ship.message.websocket"/><!-- 下面标签可以不加 等价于所有component-scan--><context:annotation-config /><!-- 这个重点,标注必须加,websocket用到--><mvc:annotation-driven/><bean id="byteArrayHttpMessageConverter"class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /><bean id="stringHttpMessageConverter"class="org.springframework.http.converter.StringHttpMessageConverter"><property name="supportedMediaTypes"><list><value>text/plain;charset=UTF-8</value></list></property></bean><bean id="jsonHttpMessageConverter"class="cn.com.ship.external.spring.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="com.fasterxml.jackson.databind.ObjectMapper"><!--对属性值为null的不序列化反序列化--><property name="serializationInclusion"><util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL"/></property></bean></property><property name="supportedMediaTypes"><list><value>application/json</value></list></property></bean><bean id="jsonHttpMessageConverter4JS"class="cn.com.ship.external.spring.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="com.fasterxml.jackson.databind.ObjectMapper"><property name="dateFormat"><bean class="java.text.SimpleDateFormat"><constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" /></bean></property><!--对属性值为null的不序列化反序列化--><property name="serializationInclusion"><util:constant static-field="com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL"/></property></bean></property><property name="supportedMediaTypes"><list><value>text/json</value></list></property></bean><bean id="viewResolver"class="org.springframework.web.servlet.view.UrlBasedViewResolver"><property name="viewClass"value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/" /><property name="suffix" value=".jsp" /></bean><!-- 文件上传配置 --><bean id="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver"p:defaultEncoding="utf-8"><property name="maxUploadSize" value="1024000000" /><property name="resolveLazily" value="true" /></bean></beans>

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

 

 

5. jsp相关Websocket脚本编写

     ws.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><html><head><title>Java API for WebSocket (JSR-356)</title>  </head><body><script type="text/javascript" src="http://localhost:8080/ship/js/jquery/jquery.min.js"></script><script type="text/javascript" src="http://localhost:8080/ship/js/sockjs-0.3.4.min.js"></script><script type="text/javascript">var websocket = null;if ('WebSocket' in window) {websocket = new WebSocket("ws://localhost:8080/ship/webSocketServer.do");} else if ('MozWebSocket' in window) {websocket = new MozWebSocket("ws://localhost:8080/ship/webSocketServer.do");} else {websocket = new SockJS("http://localhost:8080/ship/sockjs/webSocketServer.do");}websocket.onopen = onOpen;websocket.onmessage = onMessage;websocket.onerror = onError;websocket.onclose = onClose;      function onOpen(openEvt) {//alert(openEvt.Data);}function onMessage(evt) {alert(evt.data);}function onError() {}function onClose() {}function doSend() {if (websocket.readyState == websocket.OPEN) {              var msg = document.getElementById("inputMsg").value;              websocket.send(msg);//调用后台handleTextMessage方法            alert("发送成功!");          } else {          alert("连接失败!");          }  }</script>请输入:<textarea rows="5" cols="10" id="inputMsg" name="inputMsg"></textarea><button onclick="doSend();">发送</button></body></html>

    login.jsp

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Java API for WebSocket (JSR-356)</title> </head><body>        <!-ship是我的项目名--><form action="/ship/websocket/login.do">登录名:<input type="text" name="username"/><input type="submit" value="登录"/></form></body></html>

 

 

5. 调用端Controller编写 WebsocketController.java

 

package cn.com.ship.websocket.controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class WebsocketController {@Bean//这个注解会从Spring容器拿出Beanpublic InfoHandler infoHandler() {return new InfoHandler();}@RequestMapping("/websocket/login")public void login(HttpServletRequest request, HttpServletResponse response) throws Exception {String username = request.getParameter("username");HttpSession session = request.getSession(false);session.setAttribute(Constants.SESSION_USERNAME, username);response.sendRedirect("/ship/websocket/ws.jsp");}@RequestMapping("/websocket/send")@ResponseBodypublic String send(HttpServletRequest request) {String username = request.getParameter("username");infoHandler().sendMessageToUser(username, new TextMessage("你好,测试!!!!"));return null;}}

 

 

6. 测试Websocket是否连接成功

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

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

1 0
原创粉丝点击