springBoot使用webscoket,根据订阅信息发送信息给不同的用户

本文介绍了一种基于WebSocket和JPA实现的消息推送系统。该系统包括消息、接收消息及订阅消息的数据库表设计,利用WebSocket进行实时消息推送,并通过JPA持久化消息数据。文章详细阐述了WebSocket配置、用户会话管理、消息处理流程及前后端交互。

dao层使用jpa

1.数据库三张表的表结构
message表,消息信息表
asasaas在这里插入图片描述
message_receive接受消息表
在这里插入图片描述
subscrib订阅消息表
在这里插入图片描述
我想的是发送消息根据订阅表中的订阅类型发送到某某人那里,缺少一张消息类型表,如果需要可以加上

2.WebSocket配置类

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3.因为MyWebScoket中的session没有存储HttpSession中的信息,需要将HttpSession加入到MyWebScoket中,以获取用户信息,否则获取不到用户信息,

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec,
                                HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession=(HttpSession) request.getHttpSession();
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }

}

4.myWebScoket测试类,使用map用来存放每个客户端对应的MyWebSocket对象和用户名字关联,以便发送消息时
根据订阅信息查询并通知在线用户查看信息

@ServerEndpoint(value = "/websocket",configurator=GetHttpSessionConfigurator.class)
@Component
public class MyWebSocket {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteMap<String,MyWebSocket> webSocketSet = new CopyOnWriteMap<>();
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        User user = getUserInfo();
        webSocketSet.put(user.getUsername(),this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
        /*try {
            sendMessage("连接成功,当前的人数是"+getOnlineCount());
        } catch (IOException e) {
            System.out.println("IO异常");
        }*/
    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        User user = getUserInfo();
        webSocketSet.remove(user.getUsername());  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param messageJson 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String messageJson) {
        JSONObject jsonObject = JSON.parseObject(messageJson);
        Message message = jsonObject.toJavaObject(Message.class);
        //获取消息创建人和创建时间
        message.setCreator(getUserInfo().getId());
        message.setCreateTime(LocalDateTime.now());
        System.out.println("来自客户端的消息:" + message.getContent());
        //获取spring容器内的SubscribService
        SubscribService subscribService = SpringContextUtils.getBean(SubscribService.class);
        MessageService messageService = SpringContextUtils.getBean(MessageService.class);
        MessageReceiveService messageReceiveService = SpringContextUtils.getBean(MessageReceiveService.class);
        //保存消息信息到数据库
        Message messageData = messageService.save(message);
        //获取订阅信息,根据订阅信息把信息转发到指定的用户
        List<Subscrib> subscribs = subscribService.findAllByType(message.getType());

        for (Subscrib su:subscribs) {
            //新增消息接收信息
            MessageReceive messageReceive = new MessageReceive();
            messageReceive.setIsRead(0);
            messageReceive.setMessageId(messageData.getId());
            messageReceive.setCreateTime(LocalDateTime.now());
            messageReceive.setReceiver(su.getSubscriber());
            //保存消息接收信息到数据库
            messageReceiveService.save(messageReceive);
            //群发消息
            for (String sub : webSocketSet.keySet()) {
                if(su.getSubscriber().equals(sub)){
                    MyWebSocket item = webSocketSet.get(sub);
                    try {
                        item.sendMessage(message.getContent());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

     // 发生错误时调用
     @OnError
     public void onError(Throwable error) {
     System.out.println("发生错误");
     error.printStackTrace();
     }

     public void sendMessage(String message) throws IOException {
         this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
     }
    private User getUserInfo(){
        return (User)((HttpSession)session.getUserProperties().get(HttpSession.class.getName())).getAttribute("user");
    }
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }
}

获取ioc容器中bean的工具类

@Component
    public class SpringContextUtils implements ApplicationContextAware {

        /**
         * 上下文对象实例
         */
        private static ApplicationContext applicationContext;

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }

        /**
         * 获取applicationContext
         *
         * @return
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }

        /**
         * 通过name获取 Bean.
         *
         * @param name
         * @return
         */
        public static Object getBean(String name) {
            return getApplicationContext().getBean(name);
        }

        /**
         * 通过class获取Bean.
         *
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T getBean(Class<T> clazz) {
            return getApplicationContext().getBean(clazz);
        }

        /**
         * 通过name,以及Clazz返回指定的Bean
         *
         * @param name
         * @param clazz
         * @param <T>
         * @return
         */
        public static <T> T getBean(String name, Class<T> clazz) {
            return getApplicationContext().getBean(name, clazz);
        }
    }

前端页面,测试是类型输入1,页面下方js最好封装到公共js里面,主界面引入一下。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="Bookmark" href="/favicon.ico">
    <link rel="Shortcut Icon" href="/favicon.ico"/>

    <script type="text/javascript" th:src="@{/lib/jquery/1.9.1/jquery.min.js}"></script>
    <title>发送消息页面</title>
</head>
<body style="text-align: center">
Welcome<br/>
标题:<input id="title">
类型:<input id="type">
内容:<textarea id="content" rows="1" cols="10"></textarea>

<button onclick="send()">提交</button>    <!--<button onclick="closeWebSocket()">Close</button>-->

<div id="message"></div>
</body>

<script type="text/javascript">
    var websocket = null;

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

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

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

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

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

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

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

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

    //发送消息
    function send(){
        var message={};
        message['title']=$("#title").val();
        message['type']=$("#type").val();
        message['content']=$("#content").val();
        websocket.send(JSON.stringify(message));
    }
</script>
</html>

必须登录,然后将信息存储到session中
在页面上输入信息发送,在另外一个浏览器中使用订阅人登录就能自动弹出信息框了。

### Spring Boot WebSocket 发送消息到 Vue.js 前端 为了实现Spring Boot通过WebSocket向Vue.js前端发送消息,可以按照如下方法配置和编写代码。 #### 配置依赖项 在`pom.xml`文件中加入必要的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> </dependency> <dependency> <groupId>org.webjars.bowergithub.angular</groupId> <artifactId>sockjs-client</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3-1</version> </dependency> ``` 这些依赖用于支持WebSocket通信以及STOMP协议[^1]。 #### 启用WebSocket并配置消息代理 创建一个新的Java类来启用WebSocket功能,并配置一个简单的内存消息代理: ```java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").withSockJS(); } } ``` 这段代码定义了一个WebSocket连接点`/ws`并通过SockJS提供兼容性。还设置了应用程序前缀为`/app`的消息路径,以及广播给所有客户端的主题路径为`/topic`。 #### 创建控制器处理来自服务器的消息 下面是一个例子展示如何构建一个Controller用来接收HTTP请求并向特定会话ID的客户端推送通知: ```java @Controller public class GreetingController { private SimpMessagingTemplate messagingTemplate; @Autowired public GreetingController(SimpMessagingTemplate messagingTemplate){ this.messagingTemplate = messagingTemplate; } @GetMapping(value="/send/{sessionId}") @ResponseBody String sendMessage(@PathVariable String sessionId, HttpServletRequest request){ // Send a message to the specified session ID. messagingTemplate.convertAndSendToUser(sessionId,"/queue/reply","Hello " + sessionId); return "Sent"; } } ``` 此段落展示了当访问带有参数`sessionId`的GET接口时,服务端将尝试把一条问候语句推送给对应的用户。注意这里使用的是`convertAndSendToUser()`函数指定目标用户的队列地址为`/queue/reply`。 #### 客户端部分 (Vue.js) 对于Vue应用来说,在组件内部初始化Socket连接并与后端交互的方式如下所示: ```javascript import SockJS from 'sockjs-client'; import Stomp from 'stompjs'; export default { name: 'ChatApp', data() { return { stompClient: null, messages: [] }; }, mounted(){ const socket = new SockJS('http://localhost:8080/ws'); this.stompClient = Stomp.over(socket); this.stompClient.connect({}, frame => { console.log('Connected: ' + frame); // Subscribe to receive messages sent by server this.stompClient.subscribe('/user/queue/replies', message => { let msgBody = JSON.parse(message.body).content; this.messages.push(msgBody); }); }); }, methods:{ sendMessage(text){ this.stompClient.send("/app/hello", {}, text); } } }; ``` 上述JavaScript片段实现了与Spring Boot Web Socket建立连接的功能,并订阅了个人化的回复频道`/user/queue/replies`以便监听到来自服务器的信息更新。同时提供了发送消息的方法供调用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值