Spring和Websocket相結(jié)合實現(xiàn)消息的推送
本文主要有三個步驟
1、用戶登錄后建立websocket連接,默認(rèn)選擇websocket連接,如果瀏覽器不支持,則使用sockjs進行模擬連接
2、建立連接后,服務(wù)端返回該用戶的未讀消息
3、服務(wù)端進行相關(guān)操作后,推送給某一個用戶或者所有用戶新消息 相關(guān)環(huán)境 Spring4.0.6(要選擇4.0+),tomcat7.0.55
Websocet服務(wù)端實現(xiàn)
WebSocketConfig.java
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(systemWebSocketHandler(),"/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor());
registry.addHandler(systemWebSocketHandler(), "/sockjs/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor())
.withSockJS();
}
@Bean
public WebSocketHandler systemWebSocketHandler(){
return new SystemWebSocketHandler();
}
}
不要忘記在springmvc的配置文件中配置對此類的自動掃描
<context:component-scan base-package="com.ldl.origami.websocket" />
@Configuration
@EnableWebMvc
@EnableWebSocket
這三個大致意思是使這個類支持以@Bean的方式加載bean,并且支持springmvc和websocket,不是很準(zhǔn)確大致這樣,試了一下@EnableWebMvc不加也沒什么影響,@Configuration本來就支持springmvc的自動掃描
registry.addHandler(systemWebSocketHandler(),"/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor())
用來注冊websocket server實現(xiàn)類,第二個參數(shù)是訪問websocket的地址
registry.addHandler(systemWebSocketHandler(), "/sockjs/webSocketServer").addInterceptors(new WebSocketHandshakeInterceptor()) .withSockJS(); }
這個是使用Sockjs的注冊方法
首先SystemWebSocketHandler.java
public class SystemWebSocketHandler implements WebSocketHandler {
private static final Logger logger;
private static final ArrayList<WebSocketSession> users;
static {
users = new ArrayList<>();
logger = LoggerFactory.getLogger(SystemWebSocketHandler.class);
}
@Autowired
private WebSocketService webSocketService;
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.debug("connect to the websocket success......");
users.add(session);
String userName = (String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);
if(userName!= null){
//查詢未讀消息
int count = webSocketService.getUnReadNews((String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME));
session.sendMessage(new TextMessage(count + ""));
}
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
//sendMessageToUsers();
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if(session.isOpen()){
session.close();
}
logger.debug("websocket connection closed......");
users.remove(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
logger.debug("websocket connection closed......");
users.remove(session);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 給所有在線用戶發(fā)送消息
*
* @param message
*/
public void sendMessageToUsers(TextMessage message) {
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 給某個用戶發(fā)送消息
*
* @param userName
* @param message
*/
public void sendMessageToUser(String userName, TextMessage message) {
for (WebSocketSession user : users) {
if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
}
相關(guān)內(nèi)容大家一看就能明白,就不多解釋了
然后WebSocketHandshakeInterceptor.java
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
private static Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object
> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
//使用userName區(qū)分WebSocketHandler,以便定向發(fā)送消息
String userName = (String) session.getAttribute(Constants.SESSION_USERNAME);
attributes.put(Constants.WEBSOCKET_USERNAME,userName);
}
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
}
這個的主要作用是取得當(dāng)前請求中的用戶名,并且保存到當(dāng)前的WebSocketHandler中,以便確定WebSocketHandler所對應(yīng)的用戶,具體可參考HttpSessionHandshakeInterceptor
用戶登錄建立websocket連接
index.jsp
<script type="text/javascript" src="http://localhost:8080/Origami/websocket/sockjs-0.3.min.js"></script>
<script>
var websocket;
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/Origami/webSocketServer");
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8080/Origami/webSocketServer");
} else {
websocket = new SockJS("http://localhost:8080/Origami/sockjs/webSocketServer");
}
websocket.onopen = function (evnt) {
};
websocket.onmessage = function (evnt) {
$("#msgcount").html("(<font color='red'>"+evnt.data+"</font>)")
};
websocket.onerror = function (evnt) {
};
websocket.onclose = function (evnt) {
}
</script>
使用sockjs時要注意
1、這兩個的寫法
<script type="text/javascript" src="http://localhost:8080/Origami/websocket/sockjs-0.3.min.js"></script> websocket = new SockJS(http://localhost:8080/Origami/sockjs/webSocketServer);
2、web.xml中
<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_1.xsd">
version
web-app_3_1.xsd
這兩個的版本都要是3.0+
然后在這個servlet中加入
<async-supported>true</async-supported> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet>
然后所有的filter中也加入
<async-supported>true</async-supported>
3、添加相關(guān)依賴
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.3</version> </dependency>
好了,現(xiàn)在websocket可以正常建立起來了
返回用戶未讀的消息
當(dāng)連接建立后,會進入SystemWebSocketHandler的afterConnectionEstablished方法,代碼看上邊,取出WebSocketHandshakeInterceptor中保存的用戶名
查詢信息后使用session.sendMessage(new TextMessage(count + ""));返回給用戶,從哪來回哪去
服務(wù)端推送消息給用戶
@Controller
public class AdminController {
static Logger logger = LoggerFactory.getLogger(AdminController.class);
@Autowired(required = false)
private AdminService adminService;
@Bean
public SystemWebSocketHandler systemWebSocketHandler() {
return new SystemWebSocketHandler();
}
@RequestMapping("/auditing")
@ResponseBody
public String auditing(HttpServletRequest request){
//無關(guān)代碼都省略了
int unReadNewsCount = adminService.getUnReadNews(username);
systemWebSocketHandler().sendMessageToUser(username, new TextMessage(unReadNewsCount + ""));
return result;
}
}
在這里可以使用sendMessageToUser給某個用戶推送信息,也可以使用sendMessageToUsers給所有用戶推送信息
相關(guān)文章
SpringBoot創(chuàng)建多模塊項目的全過程記錄
這篇文章主要給大家介紹了關(guān)于SpringBoot創(chuàng)建多模塊項目的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Spring?cloud?OpenFeign中動態(tài)URl、動態(tài)傳遞接口地址代碼示例
openFeign是作為微服務(wù)之間調(diào)用的解決方案,每個微服務(wù)項目是必不可少的,下面這篇文章主要給大家介紹了關(guān)于Spring?cloud?OpenFeign中動態(tài)URl、動態(tài)傳遞接口地址的相關(guān)資料,需要的朋友可以參考下2024-02-02
springboot啟動后卡住無日志的幾種情況小結(jié)
這篇文章主要介紹了springboot啟動后卡住無日志的幾種情況小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
解析MapStruct轉(zhuǎn)換javaBean時出現(xiàn)的詭異事件
在項目中用到了MapStruct,對其可以轉(zhuǎn)換JavaBean特別好奇,今天小編給大家分享一個demo給大家講解MapStruct轉(zhuǎn)換javaBean時出現(xiàn)的詭異事件,感興趣的朋友一起看看吧2021-09-09
Java Swing中JList選擇事件監(jiān)聽器ListSelectionListener用法示例
這篇文章主要介紹了Java Swing中JList選擇事件監(jiān)聽器ListSelectionListener用法,結(jié)合具體實例形式分析了中JList選擇事件監(jiān)聽器ListSelectionListener的功能、使用方法及相關(guān)注意事項,需要的朋友可以參考下2017-11-11
Springboot使用RabbitMQ實現(xiàn)關(guān)閉超時訂單(示例詳解)
介紹了如何在Spring Boot項目中使用RabbitMQ實現(xiàn)訂單的延時處理和超時關(guān)閉,通過配置RabbitMQ的交換機、隊列和綁定關(guān)系,以及編寫監(jiān)聽方法,實現(xiàn)了訂單數(shù)據(jù)的發(fā)送和延時消費,感興趣的朋友一起看看吧2025-01-01
SpringBoot定時任務(wù)實現(xiàn)數(shù)據(jù)同步的方法
業(yè)務(wù)需求是,通過中臺調(diào)用api接口獲得,設(shè)備數(shù)據(jù),要求現(xiàn)實設(shè)備數(shù)據(jù)的同步,這篇文章主要介紹了SpringBoot定時任務(wù)實現(xiàn)數(shù)據(jù)同步的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08

