最近在springboot项目中用到了websocket实现待办事项的实时提醒,在集成websocket中踩了好几个坑,还好通过我的聪明才智解决了,特此记录一下。

1.引入依赖,在pom文件中加依赖。由于springboot提供了websocket的starter

springboot 接入 clickhouse springboot rsocket_websocket

    所以在pom中直接加依赖

<!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.配置websocket。因为在之后的服务器消息类中要用到用户信息,所有要提前加入用户信息。其中shiroKit是本项目中shrio的一个工具类,获取shiro用户信息的。看代码注释应该已经能够理解了吧。

/**
 * (websocket配置)
 * @author tlj
 * @date 2018年9月15日 上午10:27:58
 */
@Configuration
public class SpringWebSocketConfig extends ServerEndpointConfig.Configurator{
    
    /**
     * 修改握手,就是在握手协议建立之前修改其中携带的内容
     * @param sec
     * @param request
     * @param response
     */
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {

        sec.getUserProperties().put("user", ShiroKit.getUser());
        //sec.getUserProperties().put("name", "wb");
        super.modifyHandshake(sec, request, response);
    }
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        //这个对象说一下,貌似只有服务器是tomcat的时候才需要配置,具体我没有研究
        return new ServerEndpointExporter();
    }
}

3.服务器端消息处理类。

/**
 * @Author: tanleijin
 * @description ()
 * @Date:2018/9/14 17:45
 */
@ServerEndpoint(value = "/userMessageSocket",configurator = SpringWebSocketConfig.class)
@Component
public class UserMessageSocket {

    private static ApplicationContext applicationContext;
    private static int onlineCount = 0;
    private static CopyOnWriteArraySet<UserMessageSocket> webSocketSet = new CopyOnWriteArraySet<>();
    private Session session;
    private UserServiceImpl userService;
    //todo 这里需要一个变量来接收shiro中登录的人信息
    private ShiroUser shiroUser ;
    @OnOpen
    public void onOpen (Session session){
        this.session = session;
        //注入userService
        this.userService = applicationContext.getBean(UserServiceImpl.class);
        //设置用户
        this.shiroUser =  (ShiroUser) session.getUserProperties().get("user");
        webSocketSet.add(this);
        addOnlineCount();
        System.out.println("有新链接加入!当前在线人数为" + getOnlineCount());
    }
    @OnClose
    public void onClose (){
        webSocketSet.remove(this);
        subOnlineCount();
        System.out.println("有一链接关闭!当前在线人数为" + getOnlineCount());
    }
    @OnMessage
    public void onMessage (String message, Session session) throws IOException {
        //todo 1.制造给自己的初始化消息
        String msg = userService.createUserMessage(this.shiroUser);
        //2.发送给自己
        sendMessage (msg);
    }
    public void sendMessage (String message) throws IOException {
        //String m = "1,2,3,4,5";
        this.session.getBasicRemote().sendText(message);
    }
    /**
      * 群发自定义消息
     */
    public void sendInfo() throws IOException {
          for (UserMessageSocket item : webSocketSet) {
             try {
                 String msg = userService.createUserMessage(item.shiroUser);
                 //2.发送消息
                item.sendMessage(msg);
             } catch (IOException e) {
             continue;
         }
       }
    }
    public static synchronized  int getOnlineCount (){
        return UserMessageSocket.onlineCount;
    }
    public static synchronized void addOnlineCount (){
        UserMessageSocket.onlineCount++;
    }
    public static synchronized void subOnlineCount (){
        UserMessageSocket.onlineCount--;
    }
    public static void setApplicationContext(ApplicationContext applicationContext) {
        UserMessageSocket.applicationContext = applicationContext;
    }
}

4.前段js

var websocket = null;
$(function(){

    //获取当前网址,如: http://localhost:8083/myproj/view/my.jsp
    var curWwwPath=window.document.location.href;
    var sos=curWwwPath.indexOf("//");
    //获取主机地址,如: localhost:8083
    var localhostPaht=curWwwPath.substring(sos+2);

    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://"+localhostPaht+"userMessageSocket");
    }
    else {
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function () {
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function (event) {
        setMessageInnerHTML("open");
    }

    //接收到消息的回调方法
    websocket.onmessage = function (event) {
        onmessage(event);
    }

    //连接关闭的回调方法
    websocket.onclose = function () {
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
        websocket.close();
    }

    //发送消息
    setTimeout("websocket.send('')",500);

});

//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
    console.log(innerHTML);
}

//手动关闭连接
function closeWebSocket() {
    websocket.close();
}

//接收消息的回调方法
function onmessage(event) {
    //console.log(event.data);
    var data=event.data.split(',');

    $("#done").empty();
    $("#done").text("你有"+data[0]+"条代办");
}

5.特别注意的是在服务器端消息处理类中直接注入会失败,所以需要通过ApplicationContext对象去获取你想用的类。需要在springboot的启动类中配置:

@SpringBootApplication
public class MisApplication {

    private final static Logger logger = LoggerFactory.getLogger(MisApplication.class);

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(MisApplication.class);
        ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
        UserMessageSocket.setApplicationContext(configurableApplicationContext);//解决WebSocket不能注入的问题
        logger.info("MisApplication is success!");
    }
}

 最后总结:

         以上只是个人思路,应该还有很多解决的方法。说的再多,不如copy动手试一试,保准你一波成功,爽到爆。