前言

  之前写毕业设计的时候就想加上聊天系统,当时已经用ajax长轮询实现了一个(还不懂什么是轮询机制的,猛戳这里:https://www.cnblogs.com/hoojo/p/longPolling_comet_jquery_iframe_ajax.html),但由于种种原因没有加到毕设里面。后来回校答辩后研究了一下websocket,并参照网上资料写了一个简单的聊天,现在又重新整理并记录下来。

  以下介绍来自维基百科:
  WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

  这里可以看一下官网介绍:http://www.websocket.org/aboutwebsocket.html
  官网里面介绍非常详细,我就不做搬运工了,要是有像我一样英语不好的同学,右键->翻译成简体中文

  spring对websocket的支持:https://docs.spring.io/spring/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#websocket
  这里有一份spring对websocket的详细介绍:https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web.html#websocket

  四个大章节,内容很多,就不一一展开介绍了

  WebSocket+Java 私聊、群聊实例-LMLPHP

  效果

  2019/04/30补充:我们这个登录、登出非常简单,就一个请求地址,连页面都没有,所以刚开始我就没有贴出来,导致博客文章阅读起来比较吃力,现在在这里补充一下(由于项目后面有所改动,请求地址少了 springboot/,不过大家看得懂就行),这里只是一个小demo,所有就怎么简单怎么来

  登录 http://localhost:10086/websocket/login/huanzi,

  登出 http://localhost:10086/websocket/logout/huanzi

WebSocket+Java 私聊、群聊实例-LMLPHP    WebSocket+Java 私聊、群聊实例-LMLPHP

  上下线有提示

WebSocket+Java 私聊、群聊实例-LMLPHP

WebSocket+Java 私聊、群聊实例-LMLPHP

  如果这时候发送消息给离线的人,则会收到系统提示消息

WebSocket+Java 私聊、群聊实例-LMLPHP

  群聊

  本例中,点击自己是群聊窗口

  huanzi一发送群聊,laowang跟xiaofang都不是在当前群聊窗口,出现小圆点+1

WebSocket+Java 私聊、群聊实例-LMLPHP

  

  huanzi一发送群聊,xiaofang在当前群聊窗口,直接追加消息,老王不在对应的聊天窗口,出现小圆点+1

WebSocket+Java 私聊、群聊实例-LMLPHP

  xiaofang回复,huanzi直接追加消息,laowang依旧小圆点+1

WebSocket+Java 私聊、群聊实例-LMLPHP

  laowang点击群聊窗口,小圆点消失,追加群聊消息

WebSocket+Java 私聊、群聊实例-LMLPHP

  laowang参与群聊

WebSocket+Java 私聊、群聊实例-LMLPHP

  xiaofang切出群聊窗口,laowang在群聊发送消息,xiaofang出现小圆点+1

WebSocket+Java 私聊、群聊实例-LMLPHP

  切回来,小圆点消失,聊天数据正常接收追加

WebSocket+Java 私聊、群聊实例-LMLPHP

  三方正常参与聊天

WebSocket+Java 私聊、群聊实例-LMLPHP

  私聊

  huanzis私聊xiaofang,xiaofang聊天窗口在群聊,小圆点+1,而laowang不受影响

WebSocket+Java 私聊、群聊实例-LMLPHP

  xiaofang切到私聊窗口,小圆点消失,数据正常追加;huanzi刚好处于私聊窗口,数据直接追加

WebSocket+Java 私聊、群聊实例-LMLPHP

  效果演示到此结束,下面贴出代码

  代码编写

  首先先介绍一下项目结构

WebSocket+Java 私聊、群聊实例-LMLPHP

  maven

        <!-- springboot websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency> <!-- thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

  配置文件

#修改thymeleaf访问根路径
spring.thymeleaf.prefix=classpath:/view/
socketChart.css样式
body{
background-color: #efebdc;
}
#hz-main{
width: 700px;
height: 500px;
background-color: red;
margin: 0 auto;
} #hz-message{
width: 500px;
height: 500px;
float: left;
background-color: #B5B5B5;
} #hz-message-body{
width: 460px;
height: 340px;
background-color: #E0C4DA;
padding: 10px 20px;
overflow:auto;
} #hz-message-input{
width: 500px;
height: 99px;
background-color: white;
overflow:auto;
} #hz-group{
width: 200px;
height: 500px;
background-color: rosybrown;
float: right;
} .hz-message-list{
min-height: 30px;
margin: 10px 0;
}
.hz-message-list-text{
padding: 7px 13px;
border-radius: 15px;
width: auto;
max-width: 85%;
display: inline-block;
} .hz-message-list-username{
margin:;
} .hz-group-body{
overflow:auto;
} .hz-group-list{
padding: 10px;
} .left{
float: left;
color: #595a5a;
background-color: #ebebeb;
}
.right{
float: right;
color: #f7f8f8;
background-color: #919292;
}
.hz-badge{
width: 20px;
height: 20px;
background-color: #FF5722;
border-radius: 50%;
float: right;
color: white;
text-align: center;
line-height: 20px;
font-weight: bold;
opacity:;
}

  

socketChart.html页面
<!DOCTYPE>
<!--解决idea thymeleaf 表达式模板报红波浪线-->
<!--suppress ALL -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>聊天页面</title>
<!-- jquery在线版本 -->
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<!--引入样式-->
<link th:href="@{/css/socketChart.css}" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="hz-main">
<div id="hz-message">
<!-- 头部 -->
正在与<span id="toUserName"></span>聊天
<hr style="margin: 0px;"/>
<!-- 主体 -->
<div id="hz-message-body">
</div>
<!-- 功能条 -->
<div id="">
<button>表情</button>
<button>图片</button>
<button id="videoBut">视频</button>
<button onclick="send()" style="float: right;">发送</button>
</div>
<!-- 输入框 -->
<div contenteditable="true" id="hz-message-input"> </div>
</div>
<div id="hz-group">
登录用户:<span id="talks" th:text="${username}">请登录</span>
<br/>
在线人数:<span id="onlineCount">0</span>
<!-- 主体 -->
<div id="hz-group-body"> </div>
</div>
</div>
</body>
<script type="text/javascript" th:inline="javascript">
//项目根路径
var ctx = [[${#request.getContextPath()}]];//登录名
var username = /*[[${username}]]*/'';
</script>
<script th:src="@{/js/socketChart.js}"></script>
</html>

  

socketChart.js 逻辑代码
    //消息对象数组
var msgObjArr = new Array(); var websocket = null; //判断当前浏览器是否支持WebSocket, springboot是项目名
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:10086/springboot/websocket/"+username);
} else {
console.error("不支持WebSocket");
} //连接发生错误的回调方法
websocket.onerror = function (e) {
console.error("WebSocket连接发生错误");
}; //连接成功建立的回调方法
websocket.onopen = function () {
//获取所有在线用户
$.ajax({
type: 'post',
url: ctx + "/websocket/getOnlineList",
contentType: 'application/json;charset=utf-8',
dataType: 'json',
data: {username:username},
success: function (data) {
if (data.length) {
//列表
for (var i = 0; i < data.length; i++) {
var userName = data[i];
$("#hz-group-body").append("<div class=\"hz-group-list\"><span class='hz-group-list-username'>" + userName + "</span><span id=\"" + userName + "-status\">[在线]</span><div id=\"hz-badge-" + userName + "\" class='hz-badge'>0</div></div>");
} //在线人数
$("#onlineCount").text(data.length);
}
},
error: function (xhr, status, error) {
console.log("ajax错误!");
}
});
} //接收到消息的回调方法
websocket.onmessage = function (event) {
var messageJson = eval("(" + event.data + ")"); //普通消息(私聊)
if (messageJson.type == "1") {
//来源用户
var srcUser = messageJson.srcUser;
//目标用户
var tarUser = messageJson.tarUser;
//消息
var message = messageJson.message; //最加聊天数据
setMessageInnerHTML(srcUser.username,srcUser.username, message);
} //普通消息(群聊)
if (messageJson.type == "2"){
//来源用户
var srcUser = messageJson.srcUser;
//目标用户
var tarUser = messageJson.tarUser;
//消息
var message = messageJson.message; //最加聊天数据
setMessageInnerHTML(username,tarUser.username, message);
} //对方不在线
if (messageJson.type == "0"){
//消息
var message = messageJson.message; $("#hz-message-body").append(
"<div class=\"hz-message-list\" style='text-align: center;'>" +
"<div class=\"hz-message-list-text\">" +
"<span>" + message + "</span>" +
"</div>" +
"</div>");
} //在线人数
if (messageJson.type == "onlineCount") {
//取出username
var onlineCount = messageJson.onlineCount;
var userName = messageJson.username;
var oldOnlineCount = $("#onlineCount").text(); //新旧在线人数对比
if (oldOnlineCount < onlineCount) {
if($("#" + userName + "-status").length > 0){
$("#" + userName + "-status").text("[在线]");
}else{
$("#hz-group-body").append("<div class=\"hz-group-list\"><span class='hz-group-list-username'>" + userName + "</span><span id=\"" + userName + "-status\">[在线]</span><div id=\"hz-badge-" + userName + "\" class='hz-badge'>0</div></div>");
}
} else {
//有人下线
$("#" + userName + "-status").text("[离线]");
}
$("#onlineCount").text(onlineCount);
} } //连接关闭的回调方法
websocket.onclose = function () {
//alert("WebSocket连接关闭");
} //将消息显示在对应聊天窗口 对于接收消息来说这里的toUserName就是来源用户,对于发送来说则相反
function setMessageInnerHTML(srcUserName,msgUserName, message) {
//判断
var childrens = $("#hz-group-body").children(".hz-group-list");
var isExist = false;
for (var i = 0; i < childrens.length; i++) {
var text = $(childrens[i]).find(".hz-group-list-username").text();
if (text == srcUserName) {
isExist = true;
break;
}
}
if (!isExist) {
//追加聊天对象
msgObjArr.push({
toUserName: srcUserName,
message: [{username: msgUserName, message: message, date: NowTime()}]//封装数据
});
$("#hz-group-body").append("<div class=\"hz-group-list\"><span class='hz-group-list-username'>" + srcUserName + "</span><span id=\"" + srcUserName + "-status\">[在线]</span><div id=\"hz-badge-" + srcUserName + "\" class='hz-badge'>0</div></div>");
} else {
//取出对象
var isExist = false;
for (var i = 0; i < msgObjArr.length; i++) {
var obj = msgObjArr[i];
if (obj.toUserName == srcUserName) {
//保存最新数据
obj.message.push({username: msgUserName, message: message, date: NowTime()});
isExist = true;
break;
}
}
if (!isExist) {
//追加聊天对象
msgObjArr.push({
toUserName: srcUserName,
message: [{username: msgUserName, message: message, date: NowTime()}]//封装数据
});
}
} // 对于接收消息来说这里的toUserName就是来源用户,对于发送来说则相反
var username = $("#toUserName").text(); //刚好打开的是对应的聊天页面
if (srcUserName == username) {
$("#hz-message-body").append(
"<div class=\"hz-message-list\">" +
"<p class='hz-message-list-username'>"+msgUserName+":</p>" +
"<div class=\"hz-message-list-text left\">" +
"<span>" + message + "</span>" +
"</div>" +
"<div style=\" clear: both; \"></div>" +
"</div>");
} else {
//小圆点++
var conut = $("#hz-badge-" + srcUserName).text();
$("#hz-badge-" + srcUserName).text(parseInt(conut) + 1);
$("#hz-badge-" + srcUserName).css("opacity", "1");
}
} //发送消息
function send() {
//消息
var message = $("#hz-message-input").html();
//目标用户名
var tarUserName = $("#toUserName").text();
//登录用户名
var srcUserName = $("#talks").text();
websocket.send(JSON.stringify({
"type": "1",
"tarUser": {"username": tarUserName},
"srcUser": {"username": srcUserName},
"message": message
}));
$("#hz-message-body").append(
"<div class=\"hz-message-list\">" +
"<div class=\"hz-message-list-text right\">" +
"<span>" + message + "</span>" +
"</div>" +
"</div>");
$("#hz-message-input").html("");
//取出对象
if (msgObjArr.length > 0) {
var isExist = false;
for (var i = 0; i < msgObjArr.length; i++) {
var obj = msgObjArr[i];
if (obj.toUserName == tarUserName) {
//保存最新数据
obj.message.push({username: srcUserName, message: message, date: NowTime()});
isExist = true;
break;
}
}
if (!isExist) {
//追加聊天对象
msgObjArr.push({
toUserName: tarUserName,
message: [{username: srcUserName, message: message, date: NowTime()}]//封装数据[{username:huanzi,message:"你好,我是欢子!",date:2018-04-29 22:48:00}]
});
}
} else {
//追加聊天对象
msgObjArr.push({
toUserName: tarUserName,
message: [{username: srcUserName, message: message, date: NowTime()}]//封装数据[{username:huanzi,message:"你好,我是欢子!",date:2018-04-29 22:48:00}]
});
}
} //监听点击用户
$("body").on("click", ".hz-group-list", function () {
$(".hz-group-list").css("background-color", "");
$(this).css("background-color", "whitesmoke");
$("#toUserName").text($(this).find(".hz-group-list-username").text()); //清空旧数据,从对象中取出并追加
$("#hz-message-body").empty();
$("#hz-badge-" + $("#toUserName").text()).text("0");
$("#hz-badge-" + $("#toUserName").text()).css("opacity", "0");
if (msgObjArr.length > 0) {
for (var i = 0; i < msgObjArr.length; i++) {
var obj = msgObjArr[i];
if (obj.toUserName == $("#toUserName").text()) {
//追加数据
var messageArr = obj.message;
if (messageArr.length > 0) {
for (var j = 0; j < messageArr.length; j++) {
var msgObj = messageArr[j];
var leftOrRight = "right";
var message = msgObj.message;
var msgUserName = msgObj.username;
var toUserName = $("#toUserName").text(); //当聊天窗口与msgUserName的人相同,文字在左边(对方/其他人),否则在右边(自己)
if (msgUserName == toUserName) {
leftOrRight = "left";
} //但是如果点击的是自己,群聊的逻辑就不太一样了
if (username == toUserName && msgUserName != toUserName) {
leftOrRight = "left";
} if (username == toUserName && msgUserName == toUserName) {
leftOrRight = "right";
} var magUserName = leftOrRight == "left" ? "<p class='hz-message-list-username'>"+msgUserName+":</p>" : ""; $("#hz-message-body").append(
"<div class=\"hz-message-list\">" +
magUserName+
"<div class=\"hz-message-list-text " + leftOrRight + "\">" +
"<span>" + message + "</span>" +
"</div>" +
"<div style=\" clear: both; \"></div>" +
"</div>");
}
}
break;
}
}
}
}); //获取当前时间
function NowTime() {
var time = new Date();
var year = time.getFullYear();//获取年
var month = time.getMonth() + 1;//或者月
var day = time.getDate();//或者天
var hour = time.getHours();//获取小时
var minu = time.getMinutes();//获取分钟
var second = time.getSeconds();//或者秒
var data = year + "-";
if (month < 10) {
data += "0";
}
data += month + "-";
if (day < 10) {
data += "0"
}
data += day + " ";
if (hour < 10) {
data += "0"
}
data += hour + ":";
if (minu < 10) {
data += "0"
}
data += minu + ":";
if (second < 10) {
data += "0"
}
data += second;
return data;
}

  java代码有三个类,MyEndpointConfigure,WebSocketConfig,WebSocketServer;

  MyEndpointConfigure

/**
* 解决注入其他类的问题,详情参考这篇帖子:webSocket无法注入其他类:https://blog.csdn.net/tornadojava/article/details/78781474
*/
public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware { private static volatile BeanFactory context; @Override
public <T> T getEndpointInstance(Class<T> clazz){
return context.getBean(clazz);
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
MyEndpointConfigure.context = applicationContext;
}
}

  WebSocketConfig

/**
* WebSocket配置
*/
@Configuration
public class WebSocketConfig{ /**
* 用途:扫描并注册所有携带@ServerEndpoint注解的实例。 @ServerEndpoint("/websocket")
* PS:如果使用外部容器 则无需提供ServerEndpointExporter。
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
} /**
* 支持注入其他类
*/
@Bean
public MyEndpointConfigure newMyEndpointConfigure (){
return new MyEndpointConfigure ();
}
}

  WebSocketServer

/**
* WebSocket服务
*/
@RestController
@RequestMapping("/websocket")
@ServerEndpoint(value = "/websocket/{username}", configurator = MyEndpointConfigure.class)
public class WebSocketServer { /**
* 在线人数
*/
private static int onlineCount = 0; /**
* 在线用户的Map集合,key:用户名,value:Session对象
*/
private static Map<String, Session> sessionMap = new HashMap<String, Session>(); /**
* 注入其他类(换成自己想注入的对象)
*/
@Autowired
private UserService userService; /**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
//在webSocketMap新增上线用户
sessionMap.put(username, session); //在线人数加加
WebSocketServer.onlineCount++; //通知除了自己之外的所有人
sendOnlineCount(session, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + username + "'}");
} /**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
//下线用户名
String logoutUserName = ""; //从webSocketMap删除下线用户
for (Entry<String, Session> entry : sessionMap.entrySet()) {
if (entry.getValue() == session) {
sessionMap.remove(entry.getKey());
logoutUserName = entry.getKey();
break;
}
}
//在线人数减减
WebSocketServer.onlineCount--; //通知除了自己之外的所有人
sendOnlineCount(session, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + logoutUserName + "'}");
} /**
* 服务器接收到客户端消息时调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
try {
//JSON字符串转 HashMap
HashMap hashMap = new ObjectMapper().readValue(message, HashMap.class); //消息类型
String type = (String) hashMap.get("type"); //来源用户
Map srcUser = (Map) hashMap.get("srcUser"); //目标用户
Map tarUser = (Map) hashMap.get("tarUser"); //如果点击的是自己,那就是群聊
if (srcUser.get("username").equals(tarUser.get("username"))) {
//群聊
groupChat(session,hashMap);
} else {
//私聊
privateChat(session, tarUser, hashMap);
} //后期要做消息持久化 } catch (IOException e) {
e.printStackTrace();
}
} /**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
} /**
* 通知除了自己之外的所有人
*/
private void sendOnlineCount(Session session, String message) {
for (Entry<String, Session> entry : sessionMap.entrySet()) {
try {
if (entry.getValue() != session) {
entry.getValue().getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 私聊
*/
private void privateChat(Session session, Map tarUser, HashMap hashMap) throws IOException {
//获取目标用户的session
Session tarUserSession = sessionMap.get(tarUser.get("username")); //如果不在线则发送“对方不在线”回来源用户
if (tarUserSession == null) {
session.getBasicRemote().sendText("{\"type\":\"0\",\"message\":\"对方不在线\"}");
} else {
hashMap.put("type", "1");
tarUserSession.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(hashMap));
}
} /**
* 群聊
*/
private void groupChat(Session session,HashMap hashMap) throws IOException {
for (Entry<String, Session> entry : sessionMap.entrySet()) {
//自己就不用再发送消息了
if (entry.getValue() != session) {
hashMap.put("type", "2");
entry.getValue().getBasicRemote().sendText(new ObjectMapper().writeValueAsString(hashMap));
}
}
} /**
* 登录
*/
@RequestMapping("/login/{username}")
public ModelAndView login(HttpServletRequest request, @PathVariable String username) {
return new ModelAndView("socketChart.html", "username", username);
} /**
* 登出
*/
@RequestMapping("/logout/{username}")
public String loginOut(HttpServletRequest request, @PathVariable String username) {
return "退出成功!";
} /**
* 获取在线用户
*/
@RequestMapping("/getOnlineList")
private List<String> getOnlineList(String username) {
List<String> list = new ArrayList<String>();
//遍历webSocketMap
for (Entry<String, Session> entry : WebSocketServer.sessionMap.entrySet()) {
if (!entry.getKey().equals(username)) {
list.add(entry.getKey());
}
}
return list;
} }

  后记

  后期把所有功能都补全就完美了,表情、图片都算比较简单,之前用轮询实现的时候写过了,但是没加到这里来;音视频聊天的话可以用WbeRTC来做,之前也研究了一下,不过还没搞完,这里贴一下维基百科对它的介绍,想了解更多的自行Google:

  WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准。

  最后在加上持久化存储,注册后才能聊天,离线消息上线后接收,再加上用Redis或者其他的缓存技术支持,完美。不过聊天记录要做存储,表设计不知如何设计才合理,如果哪位大佬愿意分享可以留言给我,大家一起进步!

  补充

  2019-07-03补充:这里补充贴出pom代码,在子类引入父类,如果我们没有父类,只有一个子类,把两个整合一下就可以了

<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.huanzi.qch</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/>
</parent> <description>SpringBoot系列demo代码</description> <!-- 在父类引入一下通用的依赖 -->
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency> <!-- springboot web(MVC)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <!--lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> <!--热部署工具dev-tools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>runtime</scope>
</dependency>
</dependencies> <!--构建工具-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>${project.artifactId}</finalName>
<outputDirectory>../package</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>

parent.xml

<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>springboot-websocket</artifactId>
<version>0.0.1</version>
<name>springboot-websocket</name>
<description>SpringBoot系列——WebSocket</description> <!--继承父类-->
<parent>
<groupId>cn.huanzi.qch</groupId>
<artifactId>parent</artifactId>
<version>1.0.0</version>
</parent> <dependencies>
<!-- springboot websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency> <!-- thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

springboot-websocket.xml

  在后记的部分我们就提到要加上持久化存储,事实上我们已经开始慢慢在写一套简单的IM即时通讯,已经实现到第三版了,持续更新中...

  一套简单的web即时通讯——第一版

  一套简单的web即时通讯——第二版

  一套简单的web即时通讯——第三版

  代码开源

  代码已经开源、托管到我的GitHub、码云:

  GitHub:https://github.com/huanzi-qch/springBoot

  码云:https://gitee.com/huanzi-qch/springBoot

05-06 03:53