Java Spring WebSocket实现后端消息主动推送

这篇文章将介绍如何构建一个简单的WebSocket消息推送Demo

使用eclipse建立maven项目后引入相关的依赖jar包,如下:


xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
com.accenture
webSocket
webSocket
war
1.0.0-BUILD-SNAPSHOT

1.6
1.6.10
1.6.6
4.1.6.RELEASE
4.11
2.6.0




org.springframework
spring-context
${spring-framework.version}



commons-logging
commons-logging




org.springframework
spring-webmvc
${spring-framework.version}


org.springframework
spring-tx
${spring-framework.version}


            org.springframework
            spring-beans
            ${spring-framework.version}
       

       
            org.springframework
            spring-jdbc
            ${spring-framework.version}
       

       
            org.springframework
            spring-aop
            ${spring-framework.version}
       

       
            org.springframework
            spring-aspects
            ${spring-framework.version}
       

       
            org.springframework
            spring-orm
            ${spring-framework.version}
       

       
            org.springframework
            spring-expression
            ${spring-framework.version}
       

       
org.springframework
spring-test
${spring-framework.version}



  
   org.springframework  
   spring-websocket  
   ${spring-framework.version}  
  
  
   org.springframework  
   spring-messaging  
   ${spring-framework.version}  
  



com.fasterxml.jackson.core
jackson-core
${jackson.version}


com.fasterxml.jackson.core
jackson-databind
${jackson.version}


com.fasterxml.jackson.core
jackson-annotations
${jackson.version}




org.aspectj
aspectjrt
${org.aspectj-version}




org.slf4j
slf4j-api
${org.slf4j-version}


org.slf4j
jcl-over-slf4j
${org.slf4j-version}
runtime


org.slf4j
slf4j-log4j12
${org.slf4j-version}
runtime


log4j
log4j
1.2.15


javax.mail
mail


javax.jms
jms


com.sun.jdmk
jmxtools


com.sun.jmx
jmxri


runtime





javax.inject
javax.inject
1




javax.servlet
servlet-api
2.5
provided


javax.servlet.jsp
jsp-api
2.1
provided


javax.servlet
jstl
1.2




junit
junit
${junit.version}
test
   


   
       
           
                maven-eclipse-plugin
                2.9
               
                   
                        org.springframework.ide.eclipse.core.springnature
                   

                   
                        org.springframework.ide.eclipse.core.springbuilder
                   

                    true
                    true
               

           

           
                org.apache.maven.plugins
                maven-compiler-plugin
                2.5.1
               
                    1.6
                    1.6
                    -Xlint:all
                    true
                    true
               

           

           
                org.codehaus.mojo
                exec-maven-plugin
                1.2.1
               
                    org.test.int1.Main
               

           

       

   

Java Spring WebSocket实现后端消息主动推送_第1张图片

websocket相关类:

我们需要编写三个WebSocket的处理类,这里是利用了Spring WebSocket模块,三个类分别是WebSocket处理类SocketHandler、WebSocket配置类WebSocketConfig以及WebSocket拦截器WebSocketInterceptor。

在开始编写之前记得在Spring配置文件中添加Spring注解支持:

WebSocketConfig.java

package com.accenture.socket;


import org.springframework.beans.factory.annotation.Autowired;
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;


/**
 * @desp websocket配置
 *
 */
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{

@Autowired
private SocketHandler socketHandler;


@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//注册处理拦截器,拦截url为socketServer的请求
registry.addHandler(socketHandler, "/socketServer").addInterceptors(new WebSocketInterceptor());

//注册SockJs的处理拦截器,拦截url为/sockjs/socketServer的请求
registry.addHandler(socketHandler, "/sockjs/socketServer").addInterceptors(new WebSocketInterceptor()).withSockJS();
}


}

如上所示,在WebSocketConfig.java中,我们为SocketHandler注册了两个url请求,并应用了我们所定义的WebSocketInterceptor()拦截器。

WebSocketInterceptor.java

package com.accenture.socket;


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.HandshakeInterceptor;


/**
 * @desp websocket拦截器
 *
 */
public class WebSocketInterceptor implements HandshakeInterceptor{


@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, Exception exception) {

}

/**
* @desp 将HttpSession中对象放入WebSocketSession中
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler handler,
Map map) throws Exception {
if(request instanceof ServerHttpRequest){
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
if(session!=null){
//区分socket连接以定向发送消息
map.put("user", session.getAttribute("user"));
}
}
return true;
}


}

如上所示,在执行客户端服务器端握手之前,也就是在beforeHandshake()方法中,我们将HttpSession中我们登录后存储的对象放到WebSocketSession中,以此实现定向发送消息。

SocketHandler.java

package com.accenture.socket;


import java.io.IOException;
import java.util.ArrayList;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;


/**
 * @desp Socket处理类
 * @author [email protected]
 *
 */
@Service
public class SocketHandler implements WebSocketHandler{


private static final Logger logger;
private static final ArrayList users;

static{
users = new ArrayList();
logger = LoggerFactory.getLogger(SocketHandler.class);
}

@Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
logger.info("成功建立socket连接");
users.add(session);
String username = session.getAttributes().get("user").toString();
if(username!=null){
session.sendMessage(new TextMessage("我们已经成功建立socket通信了"));
}

}


@Override
public void handleMessage(WebSocketSession arg0, WebSocketMessage arg1)
throws Exception {
// TODO Auto-generated method stub

}


@Override
public void handleTransportError(WebSocketSession session, Throwable error)
throws Exception {
if(session.isOpen()){
session.close();
}
logger.error("连接出现错误:"+error.toString());
users.remove(session);
}

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1)
throws Exception {
logger.debug("连接已关闭");
users.remove(session);
}


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

/**
     * 给所有在线用户发送消息
     *
     * @param message
     */
    public void sendMessageToUsers(TextMessage message) {
        for (WebSocketSession user : users) {
            try {
                if (user.isOpen()) {
                    user.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * 给某个用户发送消息
     *
     * @param userName
     * @param message
     */
    public void sendMessageToUser(String userName, TextMessage message) {
        for (WebSocketSession user : users) {
            if (user.getAttributes().get("user").equals(userName)) {
                try {
                    if (user.isOpen()) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

}

3.3 SpringMVC Controller类:

SocketController.java

package com.accenture.controller;


import javax.servlet.http.HttpSession;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestMethod;
import org.springframework.web.socket.TextMessage;


import com.accenture.socket.SocketHandler;


/**
 * @desp Socket控制器
 * @author [email protected]
 * @date 2016-5-6
 *
 */
@Controller
public class SocketController{

private static final Logger logger = LoggerFactory.getLogger(SocketController.class);

@Autowired
private SocketHandler socketHandler;

@RequestMapping(value="/login")
public String login(HttpSession session){
logger.info("用户登录了建立连接啦");

session.setAttribute("user", "liulichao");

return "home";
}


@RequestMapping(value = "/message", method = RequestMethod.GET)
public String sendMessage(){

socketHandler.sendMessageToUser("liulichao", new TextMessage("这是一条测试的消息"));

return "message";
}

}

如上,在SocketController中我们定义了两个请求处理方法,首先执行login()后再session中存入user对象模拟用户已登录,而sendMessage()方法则是调用了sendMessageToUser()实现向某一个用户推送消息。

整个包结构如下

Java Spring WebSocket实现后端消息主动推送_第2张图片

同时,在web.xml中需要所有servlet与filter中需要加入异步支持:

true

前端页面实现

模拟登录页面home.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+"/";
String wsPath = "ws://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>


Home



Hello world!  This is a WebSocket demo!
















如上所示,我们访问login模拟登录后之后将跳到home.jsp,在页面中通过sockjs与服务器端我们注册的WebSocket访问接口实现握手建立socket连接。

推送消息页面message.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




message


已经发送消息了




在访问/message过后将调用消息发送的函数实现后端消息向前端的推送。

首先,我们访问login函数,将跳至home.jsp视图,出现如下的页面:

Java Spring WebSocket实现后端消息主动推送_第3张图片

在这一步中,我们已经在后台模拟登录,session中保存了user的对象,紧接着,我们再在新页面中访问message函数:

Java Spring WebSocket实现后端消息主动推送_第4张图片

这时候我们再回去看home.jsp的页面:

Java Spring WebSocket实现后端消息主动推送_第5张图片

可以看到,页面实现了更新,同时在socketServer握手请求之后并没有发生http的请求,同时我们可以在console中看到打印出来的通信的数据:

Java Spring WebSocket实现后端消息主动推送_第6张图片

至此,我们成功实现了服务器端向客户端的消息推送。



你可能感兴趣的:(Java Spring WebSocket实现后端消息主动推送)