1、首先导包

<!--websocket-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

2、WebSocketConfig.java

import org.springframework.context.annotation.Bean;
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;
import org.springframework.web.socket.handler.TextWebSocketHandler;

@Configuration
@EnableWebMvc//这个标注可以不加,如果有加,要extends WebMvcConfigurerAdapter
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //1.注册WebSocket
        String websocket_url = "/websocket/socketServer";                        //设置websocket的地址
        registry.addHandler(webSocketHandler(), websocket_url).                          //注册Handler
                addInterceptors(new WebSocketHandshakeInterceptor());                   //注册Interceptor

        //2.注册SockJS,提供SockJS支持(主要是兼容ie8)
        String sockjs_url = "/sockjs/socketServer";                              //设置sockjs的地址
        registry.addHandler(webSocketHandler(), sockjs_url).                            //注册Handler
                addInterceptors(new WebSocketHandshakeInterceptor()).                   //注册Interceptor
                withSockJS();                                                           //支持sockjs协议
    }

    @Bean
    public TextWebSocketHandler webSocketHandler() {
        return new WebSocketHandler();
    }
}

3、WebSocketHandler.java

import com.jam.pojo.WebSocketUser;
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;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class WebSocketHandler extends TextWebSocketHandler {

    // 已建立连接的用户
    private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();
    SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm:ss");
    /**
     * 处理前端发送的文本信息 js调用websocket.send时候,会调用该方法
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");

        // 获取提交过来的消息详情
        System.out.println("收到用户 " + username + " 的消息:" + message.toString());
        // 分割成id和信息内容
        String[] messageInfo = message.getPayload().split("@");
        if (messageInfo.length != 2) {

        } else {
            String target = messageInfo[0];
            String content = messageInfo[1];
            // 遍历所有已连接用户
            for (WebSocketSession user : users) {
                if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(target)) {
                    //遇到匹配用户 连接正常则发送消息
                    if (user.isOpen()) {
                        sendMessageToUser(target, new TextMessage("来自\""+username+"\"的消息:"+content+"----"+sdf.format(new Date())));
                    }else{//若异常则发送失败
                        sendMessageToUser(username, new TextMessage("对方在线异常,发送失败"+"----"+sdf.format(new Date())));
                    }
                    return;
                }
            }
            //未找到匹配用户 发送失败
            sendMessageToUser(username, new TextMessage("对方暂时不在线"+"----"+sdf.format(new Date())));
        }
    }

    /**
     * 当新连接建立的时候,被调用 连接成功时候,会触发页面上onOpen方法
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        users.add(session);
        String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
        System.out.println("用户 " + username + " Connection Established");
        session.sendMessage(new TextMessage(username + " connect"));
    }

    /**
     * 当连接关闭时被调用
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
        System.out.println("用户 " + username + " Connection closed. Status: " + status);
        users.remove(session);
    }

    /**
     * 传输错误时调用
     *
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        String username = (String) session.getAttributes().get("WEBSOCKET_USERNAME");
        if (session.isOpen()) {
            session.close();
        }
        System.out.println("用户: " + username + " websocket connection closed......");
        users.remove(session);
    }

    /**
     * 给所有在线用户发送消息
     *
     * @param message
     */
    public void sendMessageToUsers(TextMessage message) {
        for (WebSocketSession user : users) {
            try {
                if (user.isOpen()) {
                    user.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 在线用户数
     *
     */
    public List<WebSocketUser> onlineUsers() {
        List<WebSocketUser> userList = new ArrayList<WebSocketUser>();
        for (WebSocketSession user : users) {
            String username = (String) user.getAttributes().get("WEBSOCKET_USERNAME");
            userList.add(new WebSocketUser(username));
        }
        return userList;
    }

    /**
     * 给某个用户发送消息
     *
     * @param userName
     * @param message
     */
    public void sendMessageToUser(String userName, TextMessage message) {
        for (WebSocketSession user : users) {
            if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {
                try {
                    if (user.isOpen()) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }
}

4、WebSocketHandshakeInterceptor.java

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;

import javax.servlet.http.HttpSession;
import java.util.Map;

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
                String userName = (String) session.getAttribute("SESSION_USERNAME");  //这边获得登录时设置的唯一用户标识
                if (userName == null) {
                    userName = "未知" + session.getId();
                }
                attributes.put("WEBSOCKET_USERNAME", userName);  //将用户标识放入参数列表后,下一步的websocket处理器可以读取这里面的数据
            }
        }
        return true;
    }

    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        System.out.println("After Handshake");
    }
}

5、WebSocketController.java

import com.alibaba.fastjson.JSON;
import com.jam.pojo.WebSocketUser;
import com.jam.utils.Retmsg;
import com.jam.websocket.WebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.TextMessage;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping(value="/websocket",produces = "application/json; charset=utf-8")
public class WebSocketController {

    @Bean // 这个注解会从Spring容器拿出Bean
    public WebSocketHandler infoHandler() {
        return new WebSocketHandler();
    }
    WebSocketHandler ws = new WebSocketHandler();
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm:ss");

    //登录WebSocket保存用户名
    @RequestMapping("/login")
    public String login(String username,HttpServletRequest request) throws Exception {
        System.out.println(username + "登录");
        HttpSession session = request.getSession();
        session.setAttribute("SESSION_USERNAME", username);
        return Retmsg.toJsonMsg("登录成功");
    }

    //推送给所有用户的后台接口
    @RequestMapping("/sendMsgToAllUsers")
    public String sendMsgToAllUsers(String username,String str){
        String date = sdf.format(new Date());
        str+="----"+date+"[来自 \""+username+"\" 的群发信息]";
        ws.sendMessageToUsers(new TextMessage(str));
        return Retmsg.toJsonMsg("发送成功");
    }

    //获取当前在线用户名
    @RequestMapping("/onlineUsers")
    public @ResponseBody String onlineUsers(){
        List<WebSocketUser> users = ws.onlineUsers();
        return JSON.toJSONString(users);
    }

}

5、JavaScript+心跳包发送

<script type="text/javascript">
    var websocket = null;
    var url = window.location.host;
    var url2 = window.location.href;
    var username = url2.split("=")[1];
    var website = "http://"+url+"/websocket/sendMsgToAllUsers";
    if ('WebSocket' in window) {
        //Websocket的连接
        websocket = new WebSocket("ws://"+url+"/websocket/socketServer");//WebSocket对应的地址
    }
    else if ('MozWebSocket' in window) {
        //Websocket的连接
        websocket = new MozWebSocket("ws://"+url+"/websocket/socketServer");//SockJS对应的地址
    }
    else {
        //SockJS的连接
        websocket = new SockJS("http://"+url+"/sockjs/socketServer");    //SockJS对应的地址
    }
    websocket.onopen = onOpen;
    websocket.onmessage = onMessage;
    websocket.onerror = onError;
    websocket.onclose = onClose;

    function onOpen(openEvt) {
        //alert(openEvt.Data);
        heartCheck.reset().start();
    }

    function onMessage(evt) {
        $("#content").append(evt.data+"<br>"); // 接收后台发送的数据
        heartCheck.reset().start();
    }
    function onError() {
    }
    function onClose() {
    }

//给指定用户发消息
    function doSend() {
        if (websocket.readyState == websocket.OPEN) {
            websocket.send($("#targetName").val()+"@"+$("#inputMsg").val());//调用后台handleTextMessage方法
            alert("发送成功!");
        } else {

            alert("连接失败!"+websocket.readyState);
        }
    }

//给所有用户发消息
    function doSend2(){
        if (websocket.readyState == websocket.OPEN) {
            $.get(website,{username:username,str:$("#inputMsg2").val()});
            alert("发送成功!");
        } else {

            alert("连接失败!"+websocket.readyState);
        }
    }

    window.close = function () {
        websocket.onclose();
    }

    var heartCheck = {
        timeout: 55000,        // 9分钟发一次心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function(){
            var self = this;
            this.serverTimeoutObj = setInterval(function(){
                if(websocket.readyState == 1){
                    console.log("连接状态,发送消息保持连接");
                    websocket.send("heartCheck");
                    heartCheck.reset().start();    // 如果获取到消息,说明连接是正常的,重置心跳检测
                }else{
                    console.log("断开状态,尝试重连");
                    new WebSocket("ws://"+url+"/websocket/socketServer");
                }
            }, this.timeout)
        }
    }

</script>