springboot整合websocket遇到的小问题

news2024/11/14 16:38:43

今天尝试了通过springboot整合websocket来初步学习使用websocket,然后发现启动的时候报错了,发这篇文章分享一下。

springboot整合websocket的步骤很简单:

第一步:创建一个springboot项目,在这里命名为websocket

在IntelliJ IDEA里创建一个springboot项目,创建过程中只需要修改项目名和选择java的版本为8, 然后点击Next等待片刻就创建好了。

第二步:pom.xml中添加websocket的依赖

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

 不需要指定版本,默认和springboot版本一致,修改springboot版本为2.5.9,完整的配置文件如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.9</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>websocket</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <lombok.version>1.18.22</lombok.version>
        <fastjson.version>2.0.8</fastjson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <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>
            <version>${lombok.version}</version>
        </dependency>

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

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 第三步:添加websocket的配置类

 在项目的根目录下创建config包,在config包下创建一个配置类WebSocketConfig

package com.example.websocket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author heyunlin
 * @version 1.0
 */
@Configuration
public class WebSocketConfig {

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

}

第四步:使用websocket

在项目根目录下创建一个endpoint包,在endpoint包下创建一个WebSocket类,在类上添加@Component和@ServerEndpoint注解,并通过@ServerEndpoint的value属性指定请求路径,用法类似于@RequestMapping。

package com.example.websocket.endpoint;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * websocket访问路径:ws://localhost:8082/websocket/用户ID
 * @author heyunlin
 * @version 1.0
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {
    private Session session;
    private String userId;

    private final CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
    private final ConcurrentHashMap<String, Session> hashMap = new ConcurrentHashMap<>();

    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathVariable String userId) {
        this.session = session;
        this.userId = userId;

        webSockets.add(this);
        hashMap.put(userId, session);

        log.debug("和用户{}创建连接。", this.userId);
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSockets.remove(this);
        hashMap.remove(this.userId);

        log.debug("用户{}关闭了websocket连接。", this.userId);
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message) {
        log.debug("收到用户{}发送的消息:", this.userId);
    }

    /**
     * 发送错误时的处理
     * @param error Throwable
     */
    @OnError
    public void onError(Throwable error) {
        error.printStackTrace();
    }

    /**
     * 发送单条消息
     * @param userId 用户ID
     * @param message 消息内容
     */
    public void sendMessage(String userId, String message) {
        if (hashMap.containsKey(userId)) {
            Session session = hashMap.get(userId);

            if (session.isOpen()) {
                session.getAsyncRemote().sendText(message);
            }
        }
    }

}

第五步:启动项目

然后启动报错了ovo

java.lang.IllegalStateException: Failed to register @ServerEndpoint class: class com.example.websocket.endpoint.WebSocket
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoint(ServerEndpointExporter.java:159) ~[spring-websocket-5.3.15.jar:5.3.15]
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoints(ServerEndpointExporter.java:134) ~[spring-websocket-5.3.15.jar:5.3.15]
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.afterSingletonsInstantiated(ServerEndpointExporter.java:112) ~[spring-websocket-5.3.15.jar:5.3.15]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:972) ~[spring-beans-5.3.15.jar:5.3.15]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.15.jar:5.3.15]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.9.jar:2.5.9]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767) [spring-boot-2.5.9.jar:2.5.9]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:447) [spring-boot-2.5.9.jar:2.5.9]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) [spring-boot-2.5.9.jar:2.5.9]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1356) [spring-boot-2.5.9.jar:2.5.9]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1345) [spring-boot-2.5.9.jar:2.5.9]
	at com.example.websocket.WebsocketApplication.main(WebsocketApplication.java:10) [classes/:na]
Caused by: javax.websocket.DeploymentException: A parameter of type [class java.lang.String] was found on method[onOpen] of class [java.lang.reflect.Method] that did not have a @PathParam annotation
	at org.apache.tomcat.websocket.pojo.PojoMethodMapping.getPathParams(PojoMethodMapping.java:347) ~[tomcat-embed-websocket-9.0.56.jar:9.0.56]
	at org.apache.tomcat.websocket.pojo.PojoMethodMapping.<init>(PojoMethodMapping.java:221) ~[tomcat-embed-websocket-9.0.56.jar:9.0.56]
	at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:155) ~[tomcat-embed-websocket-9.0.56.jar:9.0.56]
	at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:279) ~[tomcat-embed-websocket-9.0.56.jar:9.0.56]
	at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:229) ~[tomcat-embed-websocket-9.0.56.jar:9.0.56]
	at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoint(ServerEndpointExporter.java:156) ~[spring-websocket-5.3.15.jar:5.3.15]
	... 12 common frames omitted

Disconnected from the target VM, address: '127.0.0.1:53445', transport: 'socket'

Process finished with exit code 1

重点看这行

Caused by: javax.websocket.DeploymentException: A parameter of type [class java.lang.String] was found on method[onOpen] of class [java.lang.reflect.Method] that did not have a @PathParam annotation

意思是onOpen()方法上的一个String类型的参数上没有用@PathParam注解,检查了一下,确实没有用这个注解,用的是@PathVariable,这两个注解的作用是类似的,都是获取rest风格请求的参数。修改一下onOpen()方法

package com.example.websocket.endpoint;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * websocket访问路径:ws://localhost:8082/websocket/用户ID
 * @author heyunlin
 * @version 1.0
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {
    private Session session;
    private String userId;

    private final CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
    private final ConcurrentHashMap<String, Session> hashMap = new ConcurrentHashMap<>();

    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;

        webSockets.add(this);
        hashMap.put(userId, session);

        log.debug("和用户{}创建连接。", this.userId);
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSockets.remove(this);
        hashMap.remove(this.userId);

        log.debug("用户{}关闭了websocket连接。", this.userId);
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message) {
        log.debug("收到用户{}发送的消息:", this.userId);
    }

    /**
     * 发送错误时的处理
     * @param error Throwable
     */
    @OnError
    public void onError(Throwable error) {
        error.printStackTrace();
    }

    /**
     * 发送单条消息
     * @param userId 用户ID
     * @param message 消息内容
     */
    public void sendMessage(String userId, String message) {
        if (hashMap.containsKey(userId)) {
            Session session = hashMap.get(userId);

            if (session.isOpen()) {
                session.getAsyncRemote().sendText(message);
            }
        }
    }

}

修改之后能正常启动了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day16 移除元素

题目描述 解题思路&#xff1a; 1.快慢指针&#xff0c;移除数组元素只能是下标操作&#xff1b;fast往前&#xff0c;不等于value的时候&#xff0c;赋给slow&#xff1b; 2.slow&#xff1b;返回的长度直接返回slow&#xff1b; int removeElement(int* nums, int numsSize, …

Rethinking the Role of Pre-ranking in Large-scale E-Commerce Searching system

来源&#xff1a; KDD’2023Taobao Search 文章目录 ASHASMOL训练样本训练目标蒸馏精排 总结 反思粗排在大规模电商搜索系统中的角色。 由于巨大的数据量以及对系统实时反馈的要求&#xff0c;一个典型的工业排序系统通常由这些模块组成&#xff1a;召回&#xff08;matching&…

VTR编译问题

机器环境 vmware 16.0&#xff1b; ubuntu22.04.02; 问题描述 一、在build过程中会出现一些警告&#xff0c;但没有停止&#xff1b; 二、访问 **raw.githubsercontent.com/…/…**相关网站被拒绝&#xff0c;如下图&#xff1a; 之前在 /etc/hosts 仅仅是添加了raw.github…

JS数组解构赋值变量存在依赖关系

题目随便起的&#xff0c; 在刷力扣 41.缺失的第一个正数 这个题的时候&#xff0c;出现了解构赋值的问题&#xff0c; 对于[a,b] [1,2]和[b,a][2,1]按理说都是行的通的&#xff0c;和位置没有关系&#xff0c;本质上都是进行交换 可是当我在题目中 使用[nums[nums[i]-1], nu…

SpringMVC的高频面试题

2023最新版&#xff08;持续更新&#xff09; 一、SpringMVC的高频面试题1. SpringMVC的执行流程2. SpringMVC常见的注解有哪些&#xff1f; 一、SpringMVC的高频面试题 1. SpringMVC的执行流程 前置知识 视图阶段&#xff08;JSP&#xff09;: 涉及到的重要组件&#xff1a;…

KubeSphere 社区双周报 | KubeSphere 多项更新 | 2023.06.23-07.06

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.06.23-2023.…

管道设计专业版:Pipedata-Pro Crack

Pipedata-Pro 提高了管道设计的效率并减轻了查找管道信息的负担。它可以直观、轻松地检索最新的管道和设计数据。尺寸和重量单位可以设置为公制或美制单位。 Pipedata-Pro 于 1996 年由 Zeataline Projects 推出&#xff0c;现已遍布全球&#xff0c;Pipedata-Pro 管道表的映射…

怎么判断两个文档节点时候相同?判断两个DOM节点时候相等、相同的4种方法

方法1&#xff1a; document.querySelector(html) document.querySelector(html) //true 方法2&#xff1a;compareDocumentPosition document.querySelector(html).compareDocumentPosition(document.querySelector(html)) //0 返回0就代表相同 方法3&#xff1a;isEqualNode …

编译运行ContactDiscoveryService-Icelake

下载 git clone https://codeup.aliyun.com/6306306f95064d67d44656e5/lxr1907/ContactDiscoveryService-Icelake.git或github地址 git clone https://github.com/signalapp/ContactDiscoveryService-Icelake按照Readme安装 Building git submodule init git submodule update …

每次装完 homebrew,ohmyzsh 就会报错:Insecure completion-dependent directories detected:

参考:https://zhuanlan.zhihu.com/p/313037188 这是因为在big sur安装homebrew后&#xff0c;会在/usr/local/share/生成一个zsh文件夹&#xff0c;里面包含了 因此&#xff0c;zsh文件默认设置的权限是775&#xff0c;也就是group user有writer的权利&#xff0c;zsh认为这是…

python读写excel利器:xlwings从入门到精通

python读写excel利器&#xff1a;xlwings 从入门到精通 目录 安装和使用 基础操作 操作工作簿操作工作表读取单元格写入(单元格赋值)range的操作&#xff08;range常用的api&#xff09; 写入一行或一列Excel数据(函数式&#xff09;使用range(\A1\).api.AddComment(\comments…

本质矩阵(Essential Matrix)E进行分解的过程

分解本质矩阵的过程遵循以下步骤&#xff1a; 使用奇异值分解&#xff08;SVD&#xff09;计算本质矩阵E的分解。SVD分解是一种将矩阵分解为三个矩阵的乘积的方法&#xff0c;它的形式为 E UΣV^T&#xff0c;其中U和V是正交矩阵&#xff0c;Σ是对角矩阵。此步骤的结果存储在…

Edge浏览器无法展示Stable Diffusion Control Net插件

Edge浏览器无法展示Stable Diffusion Control Net插件 最近在学习Stable Diffusion&#xff0c;需要使用到Control Net插件&#xff0c;结果通过各种方式安装成功插件后&#xff0c;浏览器页面没有展示ControlNet相关页面&#xff0c;最终换到Chorme浏览器后正常&#xff0c;猜…

【Python】绘图_pandas(1)

# -*- coding: utf-8 -*- #汉字处理 import matplotlib.pyplot as plt import numpy as np from numpy.random import randn import pandas as pd #import DataFrame from matplotlib.font_manager import FontPropertiesfont FontProperties(fnamer"c:\windows\fonts\si…

基于51单片机的电子密码锁设计

功能&#xff1a;本实例是基于51单片机的电子密码锁&#xff0c;主要硬件由51单片机最小系统&#xff0c;LCD1602液晶屏电路&#xff0c;继电器控制电路&#xff0c;AT24C02存储电路&#xff0c;LED指示灯电路&#xff0c;矩阵按键电路构成。 1.系统采用LCD1602液晶屏作为显示屏…

【软件设计原则】系统设计面试基础:CAP 与 PACELC

什么是 CAP 定理以及 PACELC 如何扩展它&#xff1f; 在分布式系统中&#xff0c;可能会发生不同类型的故障&#xff0c;例如&#xff0c;服务器可能会崩溃或永久故障&#xff0c;磁盘可能会损坏导致数据丢失&#xff0c;或者网络连接可能会丢失&#xff0c;导致系统的一部分无…

在openFoam中创建cellZone

简介 openFoam的cellZone概念十分重要&#xff0c;可以给一个区域的单元命名&#xff0c;广泛应用于设置初始场&#xff08;如气、液两相流的初始VOF分布&#xff09; 而cellZone通常由cellSet转化而来&#xff0c;本文将介绍使用cellSet定义cellZone的方法 方法 在项目的sy…

【JavaWeb】博客系统的前端页面设计

目录 前言 1、页面组成 2、实现博客列表页 &#x1f342;设置背景图片 &#x1f342;设置导航栏 &#x1f342;实现版心 &#x1f343;实现个人信息 &#x1f343;实现博客列表 3、实现博客详情页 4、编写博客的登录页 5、博客编辑页面 &#x1f342;实现标题的编辑区 …

Python学习笔记-基于socket基础的http服务端程序

通过HTTP协议可以进行通信可以规范化的进行网络间通信。下面技术第一个http服务器小程序。简单的记录第一个试手程序。 1.http通信的基本流程 整个流程对应四层网络架构&#xff1a;应用层、传输层、网络层、链路层。有的部分已经封装&#xff0c;不需要我们再行处理。 2.服务…

哈夫曼树-算法

一、霍夫曼树基本概念&#xff1a; 路径&#xff1a;从树的一个结点到另外一个结点的分支构成这两个结点的路径 结点的长度&#xff1a;两节点之间路径的分支数 树的路径长度&#xff1a;从树根到每一个结点的长度之和&#xff0c;记做TL: 结点数目相同的二叉树中&#xff0…