激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Spring整合WebSocket應用示例(上)

Spring整合WebSocket應用示例(上)

2020-04-15 11:35Bridge Li JAVA教程

以下教程是小編在參與開發公司的一個crm系統,整理些相關資料,在該系統中有很多消息推送功能,在其中用到了websocket技術。下面小編整理分享到服務器之家平臺供大家參考

以下教程是小編在參與開發公司的一個crm系統,整理些相關資料,在該系統中有很多消息推送功能,在其中用到了websocket技術。下面小編整理分享到服務器之家平臺供大家參考

1. maven依賴

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<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的配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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的定義

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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的實現

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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的實現已經算完了,但還有一個核心類(關于業務邏輯查理的類)沒有實現,下篇Spring整合WebSocket應用示例(下),我們就看怎么實現這個類。

WebSocket協議介紹

WebSocket協議是RFC-6455規范定義的一個Web領域的重要的功能:全雙工,即客戶端和服務器之間的雙向通信。它是一個令人興奮的功能,業界在此領域上已經探索很久,使用的技術包括Java Applet、XMLHttpRequest、Adobe Flash、ActiveXObject、各種Comet技術、服務器端的發送事件等。

需要理解一點,在使用WebSocket協議前,需要先使用HTTP協議用于構建最初的握手。這依賴于一個機制——建立HTTP,請求協議升級(或叫協議轉換)。當服務器同意后,它會響應HTTP狀態碼101,表示同意切換協議。假設通過TCP套接字成功握手,HTTP協議升級請求通過,那么客戶端和服務器端都可以彼此互發消息。

Spring框架4.0以上版本引入了一個新模塊,即spring-websocket模塊。它對WebSocket通信提供了支持。它兼容Java WebSocket API規范JSR-356,同時提供了額外的功能。

什么場景下該使用WebSocket

在Web應用中,客戶端和服務器端需要以較高頻率和較低延遲來交換事件時,適合用WebSocket。因此WebSocket適合財經、游戲、協作等應用場景。
對于其他應用場景則未必適合。例如,某個新聞訂閱需要顯示突發新聞,使用間隔幾分鐘的長輪詢也是可以的,這里的延遲可以接受。
即使在要求低延遲的應用場景,如果傳輸的消息數很低(比如監測網絡故障的場景),那么應該考慮使用長輪詢技術。

而只有在低延遲和高頻消息通信的場景下,選用WebSocket協議才是非常適合的。即使是這樣的應用場景,仍然存在是選擇WebSocket通信呢?又或者是選擇REST HTTP通信呢?
答案是會根據應用程序的需求而定。但是,也可能同時使用這兩種技術,把需要頻繁交換的數據放到WebSocket中實現,而把REST API作為過程性的業務的實現技術。另外,當REST API的調用中需要把某個信息廣播給多個客戶端是,也可以通過WebSocket連接來實現。

Spring框架提供了@Controller注釋和@RestController注釋,兩者都可以用于HTTP請求的處理以及WebSocket消息的處理。另外,Spring MVC的請求處理方法,或其它應用程序的請求處理方法,都可以很容易地使用WebSocket協議來廣播消息到所有感興趣的客戶端或指定用戶。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久99国产伦子精品免费 | 日日久视频 | 亚洲影院在线播放 | 色综合欧美| 久久久www免费看片 亚洲综合视频一区 | av久草 | 国产羞羞视频在线免费观看 | 精品一区二区免费 | 在线中文字幕网站 | 久久99国产精品久久99果冻传媒 | 亚洲天堂一级片 | 国产一区二区三区视频观看 | 性欧美xxxx精品xxxxrb | 玖草在线资源 | 欧美成人一二三区 | 成人啪啪18免费网站 | 国内精品久久久久久2021浪潮 | 黄色片免费在线 | 黄色片视频免费观看 | 国产一区二区三区视频观看 | 国产成人免费高清激情视频 | 国产精品刺激对白麻豆99 | 国产高清自拍一区 | 国产正在播放 | 国产99免费| 精品一区二区视频在线观看 | 天堂成人国产精品一区 | 欧美人与牲禽动交精品一区 | 一区二区三区欧美在线观看 | 狠狠99 | 在线看毛片的网站 | 国产一区二区三区色淫影院 | 日本最新免费二区三区 | 久久精品中文字幕一区二区三区 | 日韩欧美高清一区 | 精国产品一区二区三区 | 国产成人精品区 | 视频在线亚洲 | 亚洲少妇诱惑 | 亚洲国产精品一区二区久久 | 欧美一区成人 |