SpringBoot集成WebSocket遇到的问题及解决

 更新时间:2023年07月18日 14:42:27   作者:华阳余文乐  
这篇文章主要介绍了SpringBoot集成WebSocket遇到的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。

SpringBoot集成WebSocket遇到的问题

最近在项目中需要使用WebSocket,因为项目是使用的SpringBoot架构,所以集成比较简单。

上代码:

pom.xml

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

WebSocketServer.java

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.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
 * web socket 工具类
 *
 * @author alvinqiu
 * @data 2018/10/24
 */
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{type}")
public class WebSocketServer {
    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的Session对象
     */
    private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
    @OnOpen
    public void onOpen(@PathParam("type") String type, Session session) {
        SessionSet.add(session);
        log.info("WebSocket有连接加入, 请求的数据类型为: " + type);
        try {
            sendMessage("Hello WebSocket " + type);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @OnClose
    public void onClose(Session session) {
        SessionSet.remove(session);
        log.info("WebSocket有连接关闭");
    }
    @OnError
    public void onError(Throwable error) {
        log.info("WebSocket发生错误, 原因: " + error.getMessage());
    }
    @OnMessage
    public void onMessage(String message) {
        log.info("收到来自WebSocket客户端的消息: " + message);
    }
    /**
     * 发送消息给客户端
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        for (Session session : SessionSet) {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(message);
            }
        }
    }
}

WebSocketConfig.java 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
 * web socket 配置类
 *
 * @author alvinqiu
 * @data 2018/10/24
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

客户端就不写了,上面的代码也没什么好说的,相当简单,项目跑起来就完事儿咯

结果就这简单玩意儿出现了问题

项目运行

java.lang.IllegalStateException: Failed to register @ServerEndpoint class: class com.xxx.WebSocketServer$$EnhancerBySpringCGLIB$$62689f33

javax.websocket.DeploymentException: Cannot deploy POJO class [com.xxx.WebSocketServer$$EnhancerBySpringCGLIB$$62689f33] as it is not annotated with @ServerEndpoint

报错了?怎么会呢,这么简单的一个玩意儿,网上的教程不都是这么干的吗

研究下错误,标注了@ServerEndPoint注解的类注册失败,百思不得其解,百度谷歌一番,什么jdk版本、tomcat版本、需不需要注入ServerEndpointExporter等等等等,我项目情况都不是(jdk1.8、tomcat8+、springboot内置容器)

后来想想是否因为项目是多模块结构的原因,可能是jar包冲突、循环依赖,什么可能都想过,然后新建了一个springboot项目,只集成websocket试试,结果是正常运行的,版本一致,环境一致,代码一致,问题看来出在项目本身了

项目结构

  • 父工程
  • A 模块(启动模块,依赖了其他所有子模块,一些公共的配置类)
  • B 模块(业务模块)
  • ...
  • ...
  • Q 模块(WebSocket模块)

难道是WebSocket作为一个单独模块出的飞机? 这么扯的理由我也不管了,还是把它挪到了A 模块去,结果问题依旧....

前后折腾了很久,终于决定跟跟源码

恩,就是这里annotation为null,至于为何为null还是不知道,突发奇想去看看另一个springboot项目(成功案例),

恩,annotation是有值的

发现了pojo的区别

(报错)

class com.xxx.websocket.WebSocketServer$$EnhancerBySpringCGLIB$$62689f33

(成功)

class com.xxx.websocket.WebSocketServer

后面那一串是代表类被cglib转换为了代理对象,什么情况下会被转换为代理对象?

AOP!AOP!AOP!

简直是如梦初醒,虎躯一震

我的 A 模块里是写了一个全局的log日志切面

大概是这样:

/**
 * 日志切面
 *
 * @author alvinqiu
 * @date 2018/09/28
 */
@Slf4j
@Aspect
@Component
public class AspectLog {
    @Pointcut("execution(* com.xxx..*.*(..))")
    public void aspectLog() {
    }
    @Pointcut("execution(* com.xxx.api..*.*(..))")
    public void aspectApiLog() {
    }
    @Pointcut("execution(* com.xxx.service..*.*(..))")
    public void aspectServiceLog() {
    }
    @Pointcut("execution(* com.xxx.mapper..*.*(..))")
    public void aspectMapperLog() {
    }
    ...
    ...
}

问题找出来咯,WebSocketServer这个类被切咯,以至于@ServerEndPoint注解无法注入至对应的对象,导致报错

解决

AOP 排除此类

总结

1. 出问题多看看源码

2. 多与大神交流,碰撞,无论多么难以描述的问题,全世界都对,你的项目不对,也要与大神沟通,一点点的终究会得到思路

 以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 如何使用Spring工具类动态匹配url

    如何使用Spring工具类动态匹配url

    这篇文章主要介绍了如何使用Spring工具类动态匹配url,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • java正则表达式实现提取需要的字符并放入数组【ArrayList数组去重复功能】

    java正则表达式实现提取需要的字符并放入数组【ArrayList数组去重复功能】

    这篇文章主要介绍了java正则表达式实现提取需要的字符并放入数组,即基于正则的ArrayList数组去重复功能,具有一定参考借鉴价值,需要的朋友可以参考下
    2017-01-01
  • SpringBoot项目使用mybatis-plus逆向自动生成全套代码

    SpringBoot项目使用mybatis-plus逆向自动生成全套代码

    在JavaWeb工程中,每一个SSM新项目或者说是SpringBoot项目也好,都少不了model、controller、service、dao等层次的构建。使用mybatis-plus逆向可以自动生成,感兴趣的可以了解一下
    2021-09-09
  • Log4j_配置方法(全面讲解)

    Log4j_配置方法(全面讲解)

    下面小编就为大家带来一篇Log4j_配置方法(全面讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • mybatis-plus 插入修改配置默认值的实现方式

    mybatis-plus 插入修改配置默认值的实现方式

    这篇文章主要介绍了mybatis-plus 插入修改配置默认值的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Spring基于注解管理bean实现方式讲解

    Spring基于注解管理bean实现方式讲解

    很多时候我们需要根据不同的条件在容器中加载不同的Bean,或者根据不同的条件来选择是否在容器中加载某个Bean,这就是Bean的加载控制,一般我们可以通过编程式或注解式两种不同的方式来完成Bean的管理
    2023-01-01
  • SpringBoot实现接口统一前缀

    SpringBoot实现接口统一前缀

    本文主要介绍了SpringBoot实现接口统一前缀,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java8并行流中自定义线程池操作示例

    Java8并行流中自定义线程池操作示例

    这篇文章主要介绍了Java8并行流中自定义线程池操作,结合实例形式分析了并行流的相关概念、定义及自定义线程池的相关操作技巧,需要的朋友可以参考下
    2019-05-05
  • 基于SpringMVC入门案例及讲解

    基于SpringMVC入门案例及讲解

    这篇文章主要介绍了基于SpringMVC入门案例及讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • java 中clone()的使用方法

    java 中clone()的使用方法

    这篇文章主要介绍了java 中clone()的使用方法的相关资料,希望通过本文能帮助大家能掌握clone()的克隆方法,需要的朋友可以参考下
    2017-09-09

最新评论