1. 程式人生 > >Java Spring WebSocket實現後端訊息主動推送

Java Spring WebSocket實現後端訊息主動推送

這篇文章將介紹如何構建一個簡單的WebSocket訊息推送Demo

使用eclipse建立maven專案後引入相關的依賴jar包,如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.accenture</groupId>
<artifactId>webSocket</artifactId>
<name>webSocket</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
<spring-framework.version>4.1.6.RELEASE</spring-framework.version>
<junit.version>4.11</junit.version>
<jackson.version>2.6.0</jackson.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- Spring WebSocket -->
<dependency>  
   <groupId>org.springframework</groupId>  
   <artifactId>spring-websocket</artifactId>  
   <version>${spring-framework.version}</version>  
</dependency>  
<dependency>  
   <groupId>org.springframework</groupId>  
   <artifactId>spring-messaging</artifactId>  
   <version>${spring-framework.version}</version>  
</dependency>  

<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>

<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>

<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>


<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>

<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>   

</dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


websocket相關類:

我們需要編寫三個WebSocket的處理類,這裡是利用了Spring WebSocket模組,三個類分別是WebSocket處理類SocketHandler、WebSocket配置類WebSocketConfig以及WebSocket攔截器WebSocketInterceptor。

在開始編寫之前記得在Spring配置檔案中新增Spring註解支援:

<context:component-scan base-package="com.accenture.demo" />

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<String, Object> 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<WebSocketSession> users;

static{
users = new ArrayList<WebSocketSession>();
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()實現向某一個使用者推送訊息。

整個包結構如下


同時,在web.xml中需要所有servlet與filter中需要加入非同步支援:

<async-supported>true</async-supported>

前端頁面實現

模擬登入頁面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+"/";
%>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
Hello world!  This is a WebSocket demo!
<div id="message">

</div>
</h1>


<script type="text/javascript" src="js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="js/sockjs.min.js"></script>




<script type="text/javascript">

$(function(){
//建立socket連線
var sock;
if ('WebSocket' in window) {
sock = new WebSocket("<%=wsPath%>socketServer");
    } else if ('MozWebSocket' in window) {
    sock = new MozWebSocket("<%=wsPath%>socketServer");
    } else {
    sock = new SockJS("<%=basePath%>sockjs/socketServer");
    }
sock.onopen = function (e) {
console.log(e);
    };
    sock.onmessage = function (e) {
    console.log(e)
        $("#message").append("<p><font color='red'>"+e.data+"</font>")
    };
    sock.onerror = function (e) {
    console.log(e);
    };
    sock.onclose = function (e) {
    console.log(e);
    }
});

</script>


</body>

</html>

如上所示,我們訪問login模擬登入後之後將跳到home.jsp,在頁面中通過sockjs與伺服器端我們註冊的WebSocket訪問介面實現握手建立socket連線。

推送訊息頁面message.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>message</title>
</head>
<body>
<h1>已經發送訊息了</h1>

</body>

</html>

在訪問/message過後將呼叫訊息傳送的函式實現後端訊息向前端的推送。

首先,我們訪問login函式,將跳至home.jsp檢視,出現如下的頁面:


在這一步中,我們已經在後臺模擬登入,session中儲存了user的物件,緊接著,我們再在新頁面中訪問message函式:


這時候我們再回去看home.jsp的頁面:


可以看到,頁面實現了更新,同時在socketServer握手請求之後並沒有發生http的請求,同時我們可以在console中看到打印出來的通訊的資料:


至此,我們成功實現了伺服器端向客戶端的訊息推送。



相關推薦

Java Spring WebSocket實現訊息主動

這篇文章將介紹如何構建一個簡單的WebSocket訊息推送Demo使用eclipse建立maven專案後引入相關的依賴jar包,如下:<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://m

3行java代碼實現百度站長主動12

收錄 工具 eboot ont 提交 pan 下載 工具類 百度收錄 介紹 當網站新增了一個網頁之後,此時這個網頁是不能夠立馬被百度收錄的,如果想以最快的速度被百度收錄則可以使用百度站長工具中的連接提交來主動向百度提交,讓百度收錄該網頁。 本文演示java使

Asp.net SignalR 實現服務訊息實時到所有Web

ASP .NET SignalR是一個ASP .NET 下的類庫,可以在ASP .NET 的Web專案中實現實時通訊。實際上 Asp.net SignalR 2 實現 服務端訊息推送到Web端, 更加簡單

SSM框架下使用websocket實現傳送訊息至前端

本篇文章本人是根據實際專案需求進行書寫的第一版,裡面有些內容對大家或許沒有用,但是核心程式碼本人已對其做了紅色標註。文章講解我將從maven座標、HTML頁面、js檔案及後端程式碼一起書寫。 一、maven座標 <!-- WebSocket配置開始--> <dependency&g

spring websocket實現前後通訊

  專案要用websocket實現一個前後端實時通訊的功能,做完之後感觸頗多,寫個部落格回顧下整個歷程,也希望能給後面的同志有點幫助。百度網盤示例原始碼:連結:https://pan.baidu.com/s/1Gi3qRyLO-lTnkVn4MqGIJA 密碼:4ovr我使用

結合 WebService 實現訊息 主動到客戶

說明:不需要複雜的技術,不需要長輪循,還是老技術實現,上程式碼 1.訊息實體 public class NoticeModel { public string Sender { get; set; } public string

WebSocket實現前後訊息

環境 jdk8 tomcat7 谷歌瀏覽器和火狐瀏覽器(瀏覽器得支援websocket) 本文用webSocket建立一個簡單的聊天室,直接上程式碼。。。 websocket 用到jar包: <dependency> &l

v-charts 和 websocket實現數據展示動態

lob each sock sent visual 基於 例子 repr har v-charts https://v-charts.js.org/#/ ELEMENT力作: 在使用 echarts 生成圖表時,經常需要做繁瑣的數據類型轉化、修改復雜的配置項,v-

Android實戰——第三方服務之Bmob雲的服務的集成和使用(三)

第一篇 文章 href 第三方服務 log 集成 android實戰 https 分享 第三方服務之Bmob後端雲的推送服務的集成和使用(三) 事先說明:這裏的一切操作都是在集成了BmobSDK之後實現的,如果對Bmob還不了解的話,請關註我第一篇Bmob文章 步驟

php微信公眾號模板訊息主動

public function get_token(){ $m = new Common(); $appid = $m->get_conf('UNION_WECHAT_MP_APPID'); $appsec = $m->ge

springboot+rabbitmq+websocket廣播模式進行訊息實時

如何安裝rabbitmq在此就不再贅述了,直接上程式碼,使用的direct佇列模式。依賴 <dependency> <groupId>org.springframework.boot</groupId>

入坑Java,開始Spring boot 的開發之路

也有 得來 中間 調用 代碼 行記錄 廣泛 隨手記 domain 換了工作,從遊戲行業的大坑中走了出來,走向互聯網的大世界。新的公司是電商方向,電商行業萬變不離其宗,java,spring是最廣泛的技術。當然也有過一些特立獨行的,也做得很大,不過現在都基本切換到了這個方向。

Spring MVC利用Hibernate Validator實現資料校驗

        吐槽一下,網上坑好多啊!不過採坑才能學習,寫bug能力-1。 JSR 303、JSR 349與Bean Validator         籠統來說,就是Java規定了一套關於驗證器的API,

java動態編譯 (java線上執行程式碼實現原理)

需求:要實現一個web網頁中輸入java程式碼,然後能知道編譯結果以及執行結果 類似於菜鳥java線上工具的效果:https://c.runoob.com/compile/10 剛開始從什麼概念都沒有到最後封裝成一個完整的工具類,中間查閱了很多資料才瞭解其中的概念以及流程,參考文獻在文章最後面。 重點需要

Java WebSocket程式設計(二):WebSocket實現主動互動

WebSocket協議 WebSocket協議通訊機制 WebSocket協議是獨立的、基於TCP的協議。其本質是先通過HTTP/HTTPS協議進行握手後建立一個用於交換資料的TCP連線,此後伺服器端與客戶器端通過此TCP連線進行實時通訊。 WebSocket開啟握手

手把手教你實現Java許可權管理系統 篇(十三):系統備份還原

系統備份還原 在很多時候,我們需要系統資料進行備份還原。我們這裡就使用MySql的備份還原命令實現系統備份還原的功能。 新建工程 新建一個maven專案,並新增相關依賴,可以用Spring boot腳手架生成。 新建 kitty-bakcup 工程,這是一個獨立運行於後臺系統的應用程式,可以分開部署。 po

JAVA整合WebSocket,實現伺服器與客戶握手

                                      WebSocket實現伺服器與客戶端握手 自學的WebSocket途中遇到很多坑,希望需要使用的朋友可以少走彎路, 使用的環境:tomcat7.0,mysql,springMvc,spring,M

Spring+Websocket實現伺服器與Andoird通訊

本部落格伺服器端內容參考於部落格:http://www.cnblogs.com/3dianpomian/p/5902084.html。 寫這篇部落格的原因是在網上查閱了很多資料,關於websocket的介紹和程式碼很多,但是很少有統一給出伺服器端和Andori

Spring+Websocket實現訊息

本文主要有三個步驟 1、使用者登入後建立websocket連線,預設選擇websocket連線,如果瀏覽器不支援,則使用sockjs進行模擬連線 2、建立連線後,服務端返回該使用者的未讀訊息 3、服務端進行相關操作後,推送給某一個使用者或者所有使用者新訊息 相關

ssm中spring websocket 實現伺服器訊息 以及 一對一聊天

上網看了很多方式,最後覺得這種方式比較簡單易懂,這邊主要有三個類(包括註解的配置檔案)就可以實現後臺內容文末會展示結果例項,如果是你所需要的效果,直接拿去用吧~專案中複製直接用本文根據網上整理並修改!!!本文思路來自:連結現在開始。開始前請確保pom已經引入需要的包首先是配置