spring4配置websocket的简单Demo

正如你可能已经看到的,Spring Framework 4.0 第一个里程碑版本已经宣布,且我们已经发布了早期的WebSocket支持。为什么WebSocket重要呢?在web上,需要在客户端(典型如浏览器)和服务器间进行高频率低延迟的消息交换是在应用中必不可少的,它使有效的,双向的通信成为可能。常见的例子包括交易,游戏,协作,数据可视化,其他的一系列场景和用例将随时间而增加。

搭建运行环境

测试环境

spring 4.2.4
tomcat 7.0.47+

Springmvc环境的搭建

1、 引入spring-framework-4.2.4.RELEASE/libs下面的相关jar包
2、配置web.xml,引入springmvc的支持

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <display-name>springsocket</display-name>
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
        <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3、 创建springmvc.xml配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                         http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                          http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/websocket
                        http://www.springframework.org/schema/websocket/spring-websocket.xsd
                        http://www.springframework.org/schema/mvc 
                        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
    <mvc:annotation-driven />
    <mvc:default-servlet-handler />

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="contentType" value="text/html" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <context:component-scan base-package="cn.xiaoyu.springsocket,cn.xiaoyu.socket" />

    <!-- HandlerMapping-处理请求映射 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    <!-- HandlerAdapter接口-处理请求的映射 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

</beans>

4、 测试首页

创建HelloWorldController类,来测试环境是否搭建成功

@Controller
@RequestMapping(value = "/")
public class HelloWorldController {

    @RequestMapping(value="/index", method = {RequestMethod.GET})
    public String login(Model model){
        model.addAttribute("message", "Hello Roin!");
        return "index";
    }
}

springWebsocket工程搭建

WebSocketConfig类

@Configuration
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements
        WebSocketConfigurer {

    public WebSocketConfig() {

    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 用来注册websocket server实现类,第二个参数是访问websocket的地址
        registry.addHandler(systemWebSocketHandler(), "/webSocketServer").addInterceptors(new HandshakeInterceptor());
        // 使用Sockjs的注册方法
        registry.addHandler(systemWebSocketHandler(), "/sockjs/webSocketServer").addInterceptors(new HandshakeInterceptor()).withSockJS();
        System.out.println("注册spring websocket成功!");
    }

    @Bean
    public WebSocketHandler systemWebSocketHandler() {
        return new SystemWebSocketHandler();
    }

}

SystemWebSocketHandler类

@Component
public class SystemWebSocketHandler implements WebSocketHandler {
    private static final List<WebSocketSession> sessions = new ArrayList<WebSocketSession>();
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("链接webSocket成功");
        sessions.add(session);
        session.sendMessage(new TextMessage("服务端链接成功!"));
    }

    @Override
    public void handleMessage(WebSocketSession wss, WebSocketMessage<?> wsm) throws Exception {
//        TextMessage returnMessage = new TextMessage(wss.getId() + ":" + wsm.getPayload());
        TextMessage returnMessage = new TextMessage("用户"+wss.getId()+"说:" + wsm.getPayload());
        System.out.println(returnMessage.toString());
//        wss.sendMessage(returnMessage);
        for (WebSocketSession session : sessions) {
            session.sendMessage(returnMessage);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession wss, Throwable thrwbl) throws Exception {
        if(wss.isOpen()){
            wss.close();
        }
       System.out.println("WebSocket出错!");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession wss, CloseStatus cs) throws Exception {
        System.out.println("WebSocket关闭!");
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

HandshakeInterceptor类

@Component
public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request,
            ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {
        System.out.println("握手前...");
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    @Override
    public void afterHandshake(ServerHttpRequest request,
            ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception ex) {
        System.out.println("握手后...");
        super.afterHandshake(request, response, wsHandler, ex);
    }

}

index.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!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>Insert title here</title>
<base href="<%=basePath%>">
<script type="text/javascript" src="js/jquery.js"></script>
<script src="//cdn.bootcss.com/sockjs-client/1.0.3/sockjs.js"></script>
<script>
    var websocket;
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://localhost/springsocket/webSocketServer");
    } else if ('MozWebSocket' in window) {
        websocket = new MozWebSocket("ws://localhost/springsocket/webSocketServer");
    } else {
        websocket = new SockJS("http://localhost/springsocket/sockjs/webSocketServer");
    }

    websocket.onopen = function (evnt) {
        $("#msgcount").append("WebSocket链接开始!<br/>");
    };
    websocket.onmessage = function (evnt) {
           $("#msgcount").append(evnt.data+"<br/>");
    };
    websocket.onerror = function (evnt) {
        $("#msgcount").append("WebSocket链接出错!<br/>");
    };
    websocket.onclose = function (evnt) {
        $("#msgcount").append("WebSocket链接关闭!<br/>");
    };
    function send() {
        websocket.send($("#msg").val());
    }
</script>
</head>
<body>${message}
<hr/>
<textarea rows="5" cols="20" id="msg"></textarea>
<br>
<button onclick="send()">发送</button>
<hr />
<div id="msgcount"></div>
</body>
</html>

这样发布之后,WebSocket就可以运行了,运行图如下:

项目结构图如下:

错误处理

如果在ie8上面运行的话会出现如下的错误:

java.lang.IllegalStateException: A SockJsMessageCodec is required but not available

此时,需要引入Jackson相关的三个jar包:jackson-databind-2.6.4.jar, jackson-core-2.6.4.jar, jackson-annotations-2.6.4.jar这样就可以重新发布了!

参考

Spring+Websocket实现消息的推送
java.lang.IllegalStateException