Spring和websocket整合应用示例(上)
嗯,这次真的仅仅是一个入门教程,因为老夫表示自己也不会。近期老夫参与开发公司的一个CRM系统,系统中有很多消息的推送,由一个同事负责,其用到了websocket技术,老夫比较感兴趣,删繁就简,整理了一个教程,留作自己笔记,因很多原理老夫也是不甚了了,以备将来用到了有资料可查。
1. maven依赖
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>4.0.1.RELEASE</version> </dependency>
2. spring-servlet的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> ...... <!-- websocket --> <bean id="websocket" class="cn.bridgeli.websocket.WebsocketEndPoint"/> <websocket:handlers> <websocket:mapping path="/websocket" handler="websocket"/> <websocket:handshake-interceptors> <bean class="cn.bridgeli.websocket.HandshakeInterceptor"/> </websocket:handshake-interceptors> </websocket:handlers> </beans>
其中,path对应的路径就是前段通过ws协议调的接口路径
3. HandshakeInterceptor的实现
package cn.bridgeli.websocket; import cn.bridgeli.utils.UserManager; import cn.bridgeli.util.DateUtil; import cn.bridgeli.sharesession.UserInfo; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import java.util.Date; import java.util.Map; /** * @Description :创建握手(handshake)接口 * @Date : 16-3-3 */ public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{ private static final Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class); @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { logger.info("建立握手前..."); ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); UserInfo currUser = UserManager.getSessionUser(attrs.getRequest()); UserSocketVo userSocketVo = new UserSocketVo(); String email= ""; if(null != currUser){ email = currUser.getEmail(); } if(StringUtils.isBlank(email)){ email = DateUtil.date2String(new Date()); } userSocketVo.setUserEmail(email); attributes.put("SESSION_USER", userSocketVo); return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { logger.info("建立握手后..."); super.afterHandshake(request, response, wsHandler, ex); } }
因为老夫不是很懂,所以最大限度的保留原代码,这其实就是从单点登录中取出当前登录用户,转成UserSocketVo对象,放到Map中。所以接下来我们看看UserSocketVo对象的定义
4. UserSocketVo的定义
package cn.bridgeli.websocket; import org.springframework.web.socket.WebSocketSession; import java.util.Date; /** * @Description : 用户socket连接实体 * @Date : 16-3-7 */ public class UserSocketVo { private String userEmail; //用户邮箱 private Date connectionTime; //成功连接时间 private Date preRequestTime; //上次请求时间 private Date newRequestTime; //新请求时间 private Date lastSendTime = new Date(); //下架消息最近一次发送时间 private Date lastTaskSendTime = new Date(); //待处理任务最近一次发送时间 private WebSocketSession webSocketSession; //用户对应的wsSession 默认仅缓存一个 // getXX and setXX }
其中最重要的就是这个WebSocketSession这个属性了,后面我们要用到
5. WebsocketEndPoint的实现
package cn.bridgeli.websocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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; /** * @Description : websocket处理类 * @Date : 16-3-3 */ public class WebsocketEndPoint extends TextWebSocketHandler{ private static final Logger logger = LoggerFactory.getLogger(WebsocketEndPoint.class); @Autowired private NewsListenerImpl newsListener; @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); TextMessage returnMessage = new TextMessage(message.getPayload()+" received at server"); session.sendMessage(returnMessage); } /** * @Description : 建立连接后 * @param session * @throws Exception */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception{ UserSocketVo userSocketVo = (UserSocketVo)session.getAttributes().get("SESSION_USER"); if(null != userSocketVo){ userSocketVo.setWebSocketSession(session); if(WSSessionLocalCache.exists(userSocketVo.getUserEmail())){ WSSessionLocalCache.remove(userSocketVo.getUserEmail()); } WSSessionLocalCache.put(userSocketVo.getUserEmail(), userSocketVo); newsListener.afterConnectionEstablished(userSocketVo.getUserEmail()); } logger.info("socket成功建立连接..."); super.afterConnectionEstablished(session); } @Override public void afterConnectionClosed(WebSocketSession session,CloseStatus status) throws Exception{ UserSocketVo userSocketVo = (UserSocketVo)session.getAttributes().get("SESSION_USER"); if(null != userSocketVo){ WSSessionLocalCache.remove(userSocketVo.getUserEmail()); } logger.info("socket成功关闭连接..."); super.afterConnectionClosed(session, status); } }
6. WSSessionLocalCache的实现
package cn.bridgeli.websocket; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description :本地缓存WebSocketSession实例 * @Date : 16-3-7 */ public class WSSessionLocalCache implements Serializable { private static Map<String, UserSocketVo> wsSessionCache = new HashMap<>(); public static boolean exists(String userEmail){ if(!wsSessionCache.containsKey(userEmail)){ return false; }else{ return true; } } public static void put(String userEmail, UserSocketVo UserSocketVo){ wsSessionCache.put(userEmail, UserSocketVo); } public static UserSocketVo get(String userEmail){ return wsSessionCache.get(userEmail); } public static void remove(String userEmail){ wsSessionCache.remove(userEmail); } public static List<UserSocketVo> getAllSessions(){ return new ArrayList<>(wsSessionCache.values()); } }
看了其实现,作用就比较明显了吧,存放每个UserSocketVo的最新数据,其实到这里我们websocket的实现已经算完了,但还有一个核心类(关于业务逻辑查理的类)没有实现,下篇我们就看怎么实现这个类
全文完,如果本文对您有所帮助,请花 1 秒钟帮忙点击一下广告,谢谢。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/262
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/262
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
近期评论