websoket在项目中的使用
项目中要求用户A发起终止申请请求后,对应的相关权限的用户B、用户C等即时收到终止的请求,针对这个需求在项目中加入了websoket功能,下面是具体的使用过程。
1.在maven中添加jar引用
1 <dependency> 2 <groupId>org.springframework</groupId> 3 <artifactId>spring-websocket</artifactId> 4 <version>5.0.9.RELEASE</version> 5 </dependency>
2.在xml的配置,在websocket-context.xml的配置,在service-context.xml的引入
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd"> <!-- <aop:aspectj-autoproxy proxy-target-class="true" /> Enables the Spring MVC @Controller programming model --> <bean id="userWebsocket" class="com.gta.scm.websocket.UserWebsocket"> </bean> <bean id="websocket" class="com.gta.scm.websocket.WebsocketEndPoint"> <property name="list"> <list> <ref bean="userWebsocket" /> </list> </property> </bean> <websocket:handlers allowed-origins="*"> <websocket:mapping path="/websocket.do" handler="websocket"/> <websocket:handshake-interceptors> <bean class="com.gta.scm.websocket.HandshakeInterceptor"> <property name="list"> <list> <ref bean="userWebsocket" /> </list> </property> </bean> </websocket:handshake-interceptors> </websocket:handlers> </beans>
<import resource="websocket-context.xml"/>
3.在后台拦截器和处理类
会话拦截器
1 import java.util.List; 2 import java.util.Map; 3 4 import org.springframework.http.server.ServerHttpRequest; 5 import org.springframework.http.server.ServerHttpResponse; 6 import org.springframework.stereotype.Component; 7 import org.springframework.web.socket.WebSocketHandler; 8 import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; 9 10 import com.gta.scm.websocket.intercept.ServiceHandshakeInterceptor; 11 12 13 @Component 14 public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{ 15 public static final String JSESSIONID = "JSESSIONID"; 16 private List<ServiceHandshakeInterceptor> list; 17 /** 18 * 当用户连接ServerSocket时执行 19 * 通过request获取用户登陆的jsessionId的值,放入到集合attributes中 20 */ 21 @Override 22 public boolean beforeHandshake(ServerHttpRequest request, 23 ServerHttpResponse response, WebSocketHandler wsHandler, 24 Map<String, Object> attributes) throws Exception { 25 26 if(list!=null){ 27 for (ServiceHandshakeInterceptor serviceHandshakeInterceptor : list) { 28 serviceHandshakeInterceptor.beforeHandshake(request, response, wsHandler, attributes); 29 } 30 } 31 return super.beforeHandshake(request, response, wsHandler, attributes); 32 } 33 34 35 36 /** 37 * 当用户连接完ServerSocket时执行 38 */ 39 @Override 40 public void afterHandshake(ServerHttpRequest request, 41 ServerHttpResponse response, WebSocketHandler wsHandler, 42 Exception ex) { 43 if(list!=null){ 44 for (ServiceHandshakeInterceptor serviceHandshakeInterceptor : list) { 45 serviceHandshakeInterceptor.afterHandshake(request, response, wsHandler, ex); 46 } 47 } 48 super.afterHandshake(request, response, wsHandler, ex); 49 } 50 51 52 53 public List<ServiceHandshakeInterceptor> getList() { 54 return list; 55 } 56 57 58 59 public void setList(List<ServiceHandshakeInterceptor> list) { 60 this.list = list; 61 } 62 63 }
1 import java.util.Map; 2 3 import org.springframework.http.server.ServerHttpRequest; 4 import org.springframework.http.server.ServerHttpResponse; 5 import org.springframework.web.socket.WebSocketHandler; 6 7 public interface ServiceHandshakeInterceptor { 8 public void beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, 9 Map<String, Object> attributes); 10 11 public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, 12 Exception ex); 13 }
websokect处理器
1 import org.springframework.web.socket.CloseStatus; 2 import org.springframework.web.socket.TextMessage; 3 import org.springframework.web.socket.WebSocketSession; 4 5 public interface ServiceTextWebSocketHandler { 6 public void handleTextMessage(WebSocketSession session, TextMessage message) ; 7 public void afterConnectionEstablished(WebSocketSession session); 8 public void handleTransportError(WebSocketSession session, Throwable exception) ; 9 public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) ; 10 11 }
1 import java.util.List; 2 3 import org.springframework.stereotype.Component; 4 import org.springframework.web.socket.CloseStatus; 5 import org.springframework.web.socket.TextMessage; 6 import org.springframework.web.socket.WebSocketSession; 7 import org.springframework.web.socket.handler.TextWebSocketHandler; 8 9 import com.gta.scm.websocket.intercept.ServiceTextWebSocketHandler; 10 @Component 11 public class WebsocketEndPoint extends TextWebSocketHandler { 12 // 所有连接用户的WebSocketSession,键是用户的jsessionID,值是连接的会话对象 13 private List<ServiceTextWebSocketHandler> list; 14 /** 15 * 该方法在用户前端进行发送消息后进行业务处理的行为 16 */ 17 @Override 18 public void handleTextMessage(WebSocketSession session, TextMessage message) { 19 20 if (list!=null) { 21 for (ServiceTextWebSocketHandler serviceTextWebSocketHandler : list) { 22 serviceTextWebSocketHandler.handleTextMessage(session, message); 23 } 24 } 25 } 26 27 /** 28 * 该方法是连接生效后进行的处理业务行为,每个用户和服务端建立websocket链接时调用 29 */ 30 @Override 31 public void afterConnectionEstablished(WebSocketSession session) throws Exception { 32 // 与客户端完成连接后调用 33 if (list!=null) { 34 for (ServiceTextWebSocketHandler serviceTextWebSocketHandler : list) { 35 serviceTextWebSocketHandler.afterConnectionEstablished(session); 36 } 37 } 38 } 39 40 @Override 41 public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { 42 // 消息传输出错时调用 43 if (list!=null) { 44 for (ServiceTextWebSocketHandler serviceTextWebSocketHandler : list) { 45 serviceTextWebSocketHandler.handleTransportError(session,exception); 46 } 47 } 48 } 49 50 @Override 51 public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { 52 if (list!=null) { 53 for (ServiceTextWebSocketHandler serviceTextWebSocketHandler : list) { 54 serviceTextWebSocketHandler.afterConnectionClosed(session,closeStatus); 55 } 56 } 57 } 58 59 @Override 60 public boolean supportsPartialMessages() { 61 return false; 62 } 63 64 public List<ServiceTextWebSocketHandler> getList() { 65 return list; 66 } 67 68 public void setList(List<ServiceTextWebSocketHandler> list) { 69 this.list = list; 70 } 71 72 73 74 75 }
1 import java.io.IOException; 2 import java.util.HashMap; 3 import java.util.Iterator; 4 import java.util.List; 5 import java.util.Map; 6 import java.util.Map.Entry; 7 import java.util.Set; 8 9 import javax.servlet.http.Cookie; 10 11 import org.apache.commons.lang.StringUtils; 12 import org.slf4j.Logger; 13 import org.slf4j.LoggerFactory; 14 import org.springframework.http.server.ServerHttpRequest; 15 import org.springframework.http.server.ServerHttpResponse; 16 import org.springframework.http.server.ServletServerHttpRequest; 17 import org.springframework.stereotype.Component; 18 import org.springframework.web.socket.CloseStatus; 19 import org.springframework.web.socket.TextMessage; 20 import org.springframework.web.socket.WebSocketHandler; 21 import org.springframework.web.socket.WebSocketSession; 22 import org.springframework.web.socket.handler.TextWebSocketHandler; 23 24 import com.gta.scm.common.constant.CommonConstant; 25 import com.gta.scm.common.login.UserLoginRsp; 26 import com.gta.scm.websocket.intercept.ServiceHandshakeInterceptor; 27 import com.gta.scm.websocket.intercept.ServiceTextWebSocketHandler; 28 29 @Component 30 public class UserWebsocket implements ServiceTextWebSocketHandler,ServiceHandshakeInterceptor{ 31 Logger logger = LoggerFactory.getLogger(UserWebsocket.class); 32 public static final String JSESSIONID = "JSESSIONID"; 33 private static final Map<String,WebSocketSession> users = new HashMap<String,WebSocketSession>(); 34 35 /** 36 * 该方法在用户前端进行发送消息后进行业务处理的行为 37 */ 38 @Override 39 public void handleTextMessage(WebSocketSession session, TextMessage message) { 40 logger.debug("handleTextMessage..."); 41 try { 42 sendMsgToUsers(message.getPayload(), session.getId()) ; 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } 46 } 47 48 /** 49 * 该方法是连接生效后进行的处理业务行为,每个用户和服务端建立websocket链接时调用 50 */ 51 @Override 52 public void afterConnectionEstablished(WebSocketSession session) { 53 logger.debug("afterConnectionEstablished..."); 54 Map<String, Object> m = session.getAttributes(); 55 UserLoginRsp user = (UserLoginRsp) m.get(CommonConstant.LOGIN_USER); 56 users.put(user.getUserInfoId().toString(), session); 57 logger.debug("put "+user.getUserAccount()+" in sessions"); 58 } 59 60 private String getJsessionId(WebSocketSession session){ 61 Object jsessionIdObj = session.getAttributes().get(HandshakeInterceptor.JSESSIONID); 62 if (jsessionIdObj != null) { 63 return jsessionIdObj.toString(); 64 } 65 return null; 66 } 67 68 @Override 69 public void handleTransportError(WebSocketSession session, Throwable exception) { 70 logger.debug("handleTransportError..."); 71 if(session.isOpen()){ 72 try { 73 session.close(); 74 } catch (IOException e) { 75 logger.debug("handleTransportError...",e); 76 } 77 } 78 logger.debug("connection closed......"); 79 Map<String, Object> m = session.getAttributes(); 80 UserLoginRsp user = (UserLoginRsp) m.get(CommonConstant.LOGIN_USER); 81 if(user!=null && users.get(user.getUserInfoId().toString())!=null) { 82 users.remove(user.getUserInfoId().toString(), session); 83 } 84 logger.debug("remove user success......"); 85 } 86 // 一个客户端连接断开时关闭 87 @Override 88 public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) { 89 logger.debug("afterConnectionClosed..."); 90 Map<String, Object> m = session.getAttributes(); 91 UserLoginRsp user = (UserLoginRsp) m.get(CommonConstant.LOGIN_USER); 92 if(user!=null && users.get(user.getUserInfoId().toString())!=null) { 93 users.remove(user.getUserInfoId().toString(), session); 94 } 95 96 } 97 /**向所有用户发送 信息 98 * @param message 99 * @throws Exception 100 */ 101 public void sendMsgToAllUsers(String message) throws Exception{ 102 logger.debug("sendMsgToAllUsers..."); 103 Entry<String,WebSocketSession> entry = null; 104 Iterator<Entry<String,WebSocketSession>> entryIt = users.entrySet().iterator(); 105 while(entryIt.hasNext()) { 106 entry = entryIt.next(); 107 entry.getValue().sendMessage(new TextMessage(message.getBytes())); 108 } 109 } 110 111 /**向Set<String> jsessionSet 中设置的用户发送信息 112 * @param message 113 * @param jsessionSet 114 * @throws Exception 115 */ 116 public void sendMsgToUsers(String message,Set<String> jsessionSet) throws Exception{ 117 logger.debug("sendMsgToUsers..."); 118 for(String jessionId : jsessionSet) { 119 if (users.containsKey(jessionId)) { 120 users.get(jessionId).sendMessage(new TextMessage(message.getBytes())); 121 122 // ShiroAuthorizationHelper.clearAuthenticationInfo(users.get(jessionId).getId()); 123 } 124 } 125 } 126 public void sendMsgToUsers(String message,String jessionId) throws Exception{ 127 logger.debug("sendMsgToUsers..."); 128 if (users.containsKey(jessionId)) { 129 users.get(jessionId).sendMessage(new TextMessage(message.getBytes())); 130 } 131 } 132 /** 133 * 当用户连接ServerSocket时执行 134 * 通过request获取用户登陆的jsessionId的值,放入到集合attributes中 135 */ 136 @Override// 137 public void beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, 138 Map<String, Object> attributes) { 139 logger.debug("beforeHandshake..."); 140 141 if (request instanceof ServletServerHttpRequest) { 142 ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; 143 Cookie cookies[] = servletRequest.getServletRequest().getCookies(); 144 String jsessionId = this.getTicket(cookies); 145 if (!StringUtils.isNotEmpty(jsessionId)) { 146 attributes.put(JSESSIONID, this.getTicket(cookies)); 147 } 148 } 149 150 151 } 152 private String getTicket(Cookie cookies[]){ 153 String jsessionId = null; 154 if(cookies != null){ 155 for(Cookie ck: cookies){ 156 if(ck.getName().trim().equals(JSESSIONID)){ 157 jsessionId = ck.getValue(); 158 break; 159 } 160 } 161 } 162 return jsessionId; 163 } 164 /** 165 * 当用户连接完ServerSocket时执行 166 */ 167 @Override 168 public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, 169 Exception ex) { 170 171 logger.debug("afterHandshake..."); 172 } 173 174 public void sendMsgToUsers(String message, List<String> idList) throws Exception{ 175 logger.debug("sendMsgToUsers..."); 176 for(String jessionId : idList) { 177 if (users.containsKey(jessionId)) { 178 logger.debug("sendMsgToUsers..."+jessionId); 179 users.get(jessionId).sendMessage(new TextMessage(message.getBytes())); 180 181 } 182 } 183 184 } 185 }
4.在具体业务的地方调用
1 //向学生端发通知强制退出到首页 2 List<String> userList=sysMessageBaseService.getTrainingUser(usp.getTrainingId()); 3 String[] idStr = (String[])userList.toArray(new String[userList.size()]); 4 Set<String> set = new HashSet<String>(Arrays.asList(idStr)); 5 userWebsocket.sendMsgToUsers("signOut", set);
1 String[] idStr = new String[userAccount.size()]; 2 String[] ownId = new String[userAccount.size()]; 3 for (int i = 0; i < userAccount.size(); i++) { 4 String str = GsonUtils.parseJson(userAccount.get(i)); 5 JSONObject object = JSONObject.fromObject(str); 6 if (object.getString("organizationCode").equals(partyCode))// 自己方人员 7 { 8 ownId[i] = object.getString("userId"); 9 } else { 10 idStr[i] = object.getString("userId"); 11 } 12 13 } 14 String strMessage = ""; 15 String myMessage = "1"; 16 if (stateType == CommonEnum.TradingContractStatus.SEND_TERMINATION.getTypeCode()) // 发送终止 17 { 18 strMessage="apply;"+contractCode; 19 //strMessage = "对方申请终止" + contractCode + "(合同号),请尽快处理!"; 20 } else if (stateType == CommonEnum.TradingContractStatus.CONFIRMED.getTypeCode())// 终止不同意 21 { 22 strMessage="disagree;"+contractCode; 23 //strMessage = "对方不同意终止" + contractCode + "(合同号),请尽快处理!"; 24 } else if (stateType == CommonEnum.TradingContractStatus.TERMINATION.getTypeCode())// 终止不同意 25 { 26 strMessage="agree;"+contractCode; 27 //strMessage = "对方同意终止" + contractCode + "(合同号),请尽快处理!"; 28 } else if (stateType == 5)// 终止取消 29 { 30 strMessage="cancel;"+contractCode; 31 //strMessage = "对方取消终止" + contractCode + "(合同号),请尽快处理!"; 32 } 33 infoAndOut(idStr, strMessage); // 对方操作 34 infoAndOut(ownId, myMessage); // 我方刷新数据
5.websoket.js的引入与在具体页面的实现
1 /** 2 * 3 * @param $ 4 */ 5 (function($) {//每页开始直接复制 6 $.websocket = {//外部可以直接访问的对象, 要导入该文件 调用对象$.devfrw.base.demo 7 connect : function(urlPath,initOpt) {//外部可以直接访问的对象的方法, 要导入该文件 调用方法$.devfrw.base.demo.pub 8 setUrl(urlPath); 9 var opt={onopen:function(){},onmessage:function(event){},onclose:function(event){}}; 10 if(initOpt){ 11 opt= $.extend(opt, initOpt); 12 }; 13 if (!url) { 14 alert('websocket url 未设置!'); 15 return; 16 } 17 ws =new WebSocket(url); 18 ws.onopen = function () { 19 // console.log('打开链接'); 20 opt.onopen(); 21 }; 22 ws.onmessage = function (event) { 23 // console.log('消息接收'); 24 opt.onmessage(event); 25 }; 26 ws.onclose = function (event) { 27 // console.log('关闭链接'); 28 opt.onclose(event); 29 }; 30 }, 31 setUrl:function(urlPath){ 32 setUrl(urlPath); 33 }, 34 send:function(message){ 35 if (ws != null) { 36 ws.send(message); 37 } else { 38 alert('websocket链接不存在!'); 39 } 40 }, 41 disconnect:function(){ 42 if (ws != null) { 43 ws.close(); 44 ws = null; 45 } 46 } 47 }; 48 var ws = null; 49 var url = null; 50 function setUrl(urlPath) { 51 if (window.location.protocol == 'http:') { 52 url = 'ws://' + window.location.host + urlPath; 53 } else { 54 url = 'wss://' + window.location.host + urlPath; 55 } 56 } 57 })(jQuery);//直接复制 一定要;号
1 $.websocket.connect('/portal/websocket.do', { 2 onmessage : function(event) {//接收数据 3 if(event.data!=""&&event.data!=null) 4 { 5 if (contains(event.data, "clear")) { 6 $(".pay").each(function() { 7 var id = $(this).parent().attr("id"); 8 var salescode = $(this).parent().attr("salescode"); 9 if (contains(event.data, id + ";")) { 10 $("#send" + id).removeClass("disabled"); 11 $("#send" + id).addClass("send"); 12 $("#pay" + id).removeClass("pay"); 13 $("#pay" + id).addClass("disabled"); 14 $("#delete" + id).removeClass("disabled"); 15 $("#delete" + id).attr("onclick","del("+salescode+")"); 16 } 17 }); 18 } 19 if (contains(event.data, "signOut")) { 20 layer.msg("轮次结束请返回首页!", { 21 time: 5000, 22 shade: 0.6, 23 success: function (layero, index) { 24 var msg = layero.text(); 25 var i = 5; 26 var timer = null; 27 var fn = function () { 28 layero.find(".layui-layer-content").text(msg + '(倒计时' + i + '秒)'); 29 if (!i) { 30 layer.close(index); 31 clearInterval(timer); 32 } 33 i--; 34 }; 35 timer = setInterval(fn, 1000); 36 fn(); 37 }, 38 }, function () { 39 window.location.href="${basePath}student/main/index"; 40 }); 41 42 } 43 } 44 45 }, 46 onclose : function(event) { 47 $.websocket.disconnect(); 48 } 49 });