You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin 实现 STOMP 聊天服务启动报错及功能实现问题咨询

问题1:启动报错修复方案

报错根本原因是你直接把携带不可序列化WebSocketSession的DTO类交给JPA作为持久化实体使用,且没有添加JPA实体相关注解,JPA无法识别该类为托管类型。修复步骤如下:

    1. 拆分实体类和DTO类:新建entity包,创建JPA持久化实体类ChatRoom,仅保留需要存入数据库的字段:
package com.example.stompserver.chat.entity

import jakarta.persistence.Entity
import jakarta.persistence.Id

@Entity
class ChatRoom {
    @Id
    var roomId: String = ""
    var name: String = ""
}
    1. 修改ChatRoomRepository关联实体类,不再关联DTO:
package com.example.stompserver.chat.repository

import com.example.stompserver.chat.entity.ChatRoom
import org.springframework.data.jpa.repository.JpaRepository

interface ChatRoomRepository: JpaRepository<ChatRoom, String> {
}
    1. 调整ChatRoomService,内存存储房间会话集合(WebSocketSession属于运行时临时对象,不可序列化存入数据库):
package com.example.stompserver.chat.service

import com.example.stompserver.chat.entity.ChatRoom
import com.example.stompserver.chat.repository.ChatRoomRepository
import org.springframework.stereotype.Service
import org.springframework.web.socket.WebSocketSession
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet

@Service
class ChatRoomService(
    private val chatRoomRepository: ChatRoomRepository
) {
    // 内存存储房间对应的会话集合,线程安全实现
    private val roomSessions = ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketSession>>()

    fun findAllRooms(): List<ChatRoom> {
        return chatRoomRepository.findAll()
    }

    fun createChatRoom(name: String): ChatRoom {
        val room = ChatRoom().apply {
            roomId = UUID.randomUUID().toString()
            this.name = name
        }
        chatRoomRepository.save(room)
        // 初始化该房间的会话集合
        roomSessions[room.roomId] = CopyOnWriteArraySet()
        return room
    }

    // 可按需添加会话增删方法
    fun addSession(roomId: String, session: WebSocketSession) {
        roomSessions[roomId]?.add(session)
    }
}

修改完成后启动报错即可解决。

问题2:现有代码优化点
    1. 给RoomController的create方法添加请求映射注解,原代码没有绑定路由,无法接收请求:
@PostMapping("/create")
fun create(@RequestParam name: String): ChatRoom {
    return chatRoomService.createChatRoom(name)
}

删除原代码中的重定向逻辑,API服务不需要返回服务端渲染视图,直接返回JSON格式数据即可。

    1. 删除原ChatRoomDTO中冗余的create实例方法,改为伴生对象工厂方法或者直接用构造函数初始化,避免不必要的对象创建开销。
    1. 所有DTO类改用data class定义,自动生成equals/hashCode/toString方法,更适合Kotlin数据传输场景:
data class ChatMessageDTO(
    var roomId: String = "",
    var writer: String = "",
    var message: String = ""
)
    1. 给接口请求添加参数校验,避免非法参数导致的服务端异常。
    1. WebSocket端点可按需关闭SockJS支持,方便原生STOMP客户端连接:registry.addEndpoint("/ws").setAllowedOriginPatterns("*"),生产环境请严格配置跨域地址。
问题3:服务端主动发消息实现与Postman 500错误修复

Postman 500错误原因及修复

  • 原create方法没有添加@PostMapping注解,请求无法匹配路由,接口不存在导致500。
  • 原代码启动报错导致服务未正常运行,请求无法处理。
    修复上述两个问题后,Postman发送POST请求到/chat/create?name=测试房间即可正常创建房间,返回房间信息。
    如果需要调用STOMP消息接口,Postman需使用WebSocket类型的请求,先连接ws://localhost:8080/ws,再按照STOMP协议格式发送消息到/pub/chat/message路径,不要用普通HTTP请求调用@MessageMapping的接口,该类接口仅接收STOMP协议消息,不响应HTTP请求。

服务端主动发消息实现

直接在任意Spring管理的Bean中注入SimpMessagingTemplate,调用convertAndSend方法即可向指定路径的订阅者推送消息,示例如下:

@Service
class SystemNotifyService(
    private val template: SimpMessagingTemplate
) {
    // 示例:向指定房间发送系统通知
    fun sendSystemNotify(roomId: String, content: String) {
        val message = ChatMessageDTO(
            roomId = roomId,
            writer = "系统通知",
            message = content
        )
        template.convertAndSend("/sub/chat/room/${roomId}", message)
    }
}

不管是定时任务触发、其他业务逻辑触发,都可以直接调用该类方法实现主动推送,不需要客户端主动发起请求。

内容的提问来源于stack exchange,提问作者pijyuuuuu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 01:12:01