WebSocket+Java 私聊、群聊实例

   前言

  之前写毕业设计的时候就想加上聊天系统,当时已经用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

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

  

 

  效果

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

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

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

    

  上下线有提示

 

 

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

 

  群聊

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

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

 

  

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

 

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

 

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

 

  laowang参与群聊

 

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

 

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

 

  三方正常参与聊天

 

  私聊

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

 

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

 

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

 

  代码编写

  首先先介绍一下项目结构

 

  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: 0;
}

.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: 0;
}

 

  

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

 

posted @ 2018-11-01 15:28  huanzi-qch  阅读(12294)  评论(12编辑  收藏  举报