Spring后端WebSocket无法从外部类触发的问题求助
问题:Spring WebSocket外部触发推送无响应
我用Spring后端搭配Angular+Ionic前端搭建了WebSocket,目的是Moodle中教师修改成绩时触发前端通知。前端已正确订阅WebSocket:
constructor(private apiUserService: ApiUserService) { this.webSocket = new WebSocket('ws://localhost:8080/newGrade'); this.webSocket.onmessage = (event) => { console.log(event.data) } }
前端能正常接收连接建立后的第一条消息,但更新数据库并调用Handler触发推送时,前端无响应。已知数据库更新成功,Moodle插件及相关方法运行正常。
后端相关代码如下:
控制层代码
@Autowired private NewGradeHandler newGradeHandler; private final ObjectMapper objectMapper = new ObjectMapper(); @PostMapping("/updateGrade") public ResponseEntity<String> miEndpoint(@RequestBody Map<String, Object> datos) throws Exception { try { // 获取请求数据 Long userId = Long.parseLong(datos.get("userid").toString()); if (userId != -1) { Long courseId = Long.parseLong(datos.get("courseid").toString()); int mark = Integer.parseInt(datos.get("grade").toString()); String assignmentName = datos.get("assignmentname").toString(); // 查询用户和科目 User student = userService.findById(userId); Subject subject = subjectService.getSubject(courseId); // 创建新成绩或更新已有成绩 if (!subjectMarkService.existsByStudentIdAndSubjectIdAndNameMark(student, subject, assignmentName)) { Subject_Mark newMark = new Subject_Mark(student, subject, mark, "Ordinaria", assignmentName); subjectMarkService.save(newMark); Subject_Mark updatedGrade = subjectMarkService.getLastSubjectMarkAdded(); sendMessageToWebSocket(updatedGrade); } else { updateExistingMark(student, subject, mark, assignmentName); } return ResponseEntity.ok("Nota creada/actualizada correctamente"); } return ResponseEntity.ok("ok"); } catch (NumberFormatException e) { return ResponseEntity.badRequest().body("Error al convertir los datos"); } catch (RuntimeException e) { return ResponseEntity.badRequest().body(e.getMessage()); } } private void updateExistingMark(User student, Subject subject, int mark, String assignmentName) throws Exception { Subject_Mark existingMark = subjectMarkService.findByStudentIdAndSubjectIdAndNameMark(student, subject, assignmentName).get(); existingMark.setMark(mark); subjectMarkService.save(existingMark); } private void sendMessageToWebSocket(Subject_Mark grade) throws Exception { TextMessage message = new TextMessage(objectMapper.writeValueAsString(grade)); for (WebSocketSession session : newGradeHandler.getSessions()) { session.sendMessage(message); }
WebSocket配置类
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new NewGradeHandler(), "/newGrade").setAllowedOrigins("*"); } @Bean public WebSocketHandler NewGradeHandler() { return new NewGradeHandler(); }}
WebSocket处理器类
@Component public class NewGradeHandler extends TextWebSocketHandler { private final ObjectMapper objectMapper = new ObjectMapper(); private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Autowired private SubjectMarkService subjectMarkService; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { Subject_Mark newGrade = subjectMarkService.getLastSubjectMarkAdded(); TextMessage message = new TextMessage(objectMapper.writeValueAsString(newGrade)); session.sendMessage(message); sessions.add(session); } public List<WebSocketSession> getSessions() { return sessions; }}
已查阅相关指南,但大多是类内部更新WebSocket的场景(这类场景可正常运行),现在需要外部类触发WebSocket的解决方案。
问题分析与解决方案
核心问题1:更新已有成绩时未触发推送
控制层的updateExistingMark方法执行完成绩更新后,没有调用sendMessageToWebSocket发送更新后的成绩数据,这是更新操作前端无响应的直接原因。
修改updateExistingMark方法,添加推送逻辑:
private void updateExistingMark(User student, Subject subject, int mark, String assignmentName) throws Exception { Subject_Mark existingMark = subjectMarkService.findByStudentIdAndSubjectIdAndNameMark(student, subject, assignmentName).get(); existingMark.setMark(mark); subjectMarkService.save(existingMark); // 添加推送逻辑 sendMessageToWebSocket(existingMark); }
核心问题2:WebSocket处理器实例不一致
WebSocketConfig中,registerWebSocketHandlers方法直接new NewGradeHandler()创建实例,而@Bean方法又返回一个全新的NewGradeHandler对象。这导致控制层注入的newGradeHandler和实际处理WebSocket连接的实例不是同一个,sessions列表为空,推送时遍历不到有效连接。
修改WebSocketConfig,使用@Bean返回的实例注册handler:
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private NewGradeHandler newGradeHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // 使用注入的实例,而非新建对象 registry.addHandler(newGradeHandler, "/newGrade").setAllowedOrigins("*"); } @Bean public WebSocketHandler newGradeHandler() { return new NewGradeHandler(); } }
额外优化建议
- 推送消息时添加异常捕获,避免单个session发送失败导致整个推送中断:
private void sendMessageToWebSocket(Subject_Mark grade) throws Exception { TextMessage message = new TextMessage(objectMapper.writeValueAsString(grade)); for (WebSocketSession session : newGradeHandler.getSessions()) { try { if (session.isOpen()) { session.sendMessage(message); } else { // 移除已关闭的session newGradeHandler.getSessions().remove(session); } } catch (IOException e) { // 记录日志,不中断其他推送 e.printStackTrace(); } } }
- 在
NewGradeHandler中重写afterConnectionClosed方法,移除关闭的session,避免无效连接占用资源:
@Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); super.afterConnectionClosed(session, status); }
内容的提问来源于stack exchange,提问作者Jesús Mariscal
相关产品推荐
相关产品推荐

