【Spring源码】WebSocket做推送动作的底层实例

一、前瞻

Ok,开始我们今天的对Spring的【模块阅读】。

那就挑Web里的WebSocket模块 ,先思考下本次阅读的阅读线索

  1. WebSocket在Spring里起到什么作用
  2. 这个模块采用了什么设计模式
  3. 我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁

二、探索

我们先整体看下整个模块的组织机构。

要解决阅读线索3 ,看起来应该从上图里的server模块开始。

找了一圈发现并没有什么线索,那我们就从阅读官方文档这个方向入手。

官方文档表面心跳采用了SckJS协议,所以我们应该从图2里的sockjs模块开始探索。

既然是发送消息给用户,那必定有send的动作,我们在sockjs模块全局搜索下。

我们根据搜索定位到了发送消息的WebSocketSession接口

java 复制代码
	/**
	 * Send a WebSocket message: either {@link TextMessage} or {@link BinaryMessage}.
	 * <p><strong>Note:</strong> The underlying standard WebSocket session (JSR-356) does
	 * not allow concurrent sending. Therefore, sending must be synchronized. To ensure
	 * that, one option is to wrap the {@code WebSocketSession} with the
	 * {@link org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator
	 * ConcurrentWebSocketSessionDecorator}.
	 * @see org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator
	 */
	void sendMessage(WebSocketMessage<?> message) throws IOException;

整体看下该接口的类图,看下是哪些具体的子类对象实现了sendMessage接口。

我们找到了以上链条的sendMessage具体实现:

java 复制代码
	@Override
	public final void sendMessage(WebSocketMessage<?> message) throws IOException {
		Assert.state(!isClosed(), "Cannot send a message when session is closed");
		Assert.isInstanceOf(TextMessage.class, message, "SockJS supports text messages only");
		sendMessageInternal(((TextMessage) message).getPayload());
	}

发现sockjs确实是像官方文档所说的,用户客户端发送心跳给服务器,服务器再使用以上的sendMessageInternal保存心跳。并不是我们要找的阅读线索3。

我们找下WebSocketSession接口 的其他子类链条实现,发现真正发送消息给用户的应该是AbstractWebSocketSession

可以在代码里找到发送各种消息格式的实现。

到这我们就解决了阅读线索3:

推送动作的底层实例究竟是谁

而关于阅读线索2:WebSocket模块采用了什么设计模式 ,我们在AbstractWebSocketHandler发现了以下代码块。

java 复制代码
public abstract class AbstractWebSocketHandler implements WebSocketHandler {

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
	}

	@Override
	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
		if (message instanceof TextMessage textMessage) {
			handleTextMessage(session, textMessage);
		}
		else if (message instanceof BinaryMessage binaryMessage) {
			handleBinaryMessage(session, binaryMessage);
		}
		else if (message instanceof PongMessage pongMessage) {
			handlePongMessage(session, pongMessage);
		}
		else {
			throw new IllegalStateException("Unexpected WebSocket message type: " + message);
		}
	}

	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
	}

	protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
	}

	protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
	}

	@Override
	public boolean supportsPartialMessages() {
		return false;
	}

}

可以看到handleMessage 方法定义了每个消息格式采用不同的消息处理方法,而这些方法该类并没有实现,而是留给了子类去实现。

也就是说handleMessage 方法定义了模板,而具体实现留给了不同子类。

这其实就是采用了模板方法模式

三、总结

而阅读线索1:WebSocket在Spring里起到什么作用,我们可以在官方文档里找到答案。

未完待续。。。

好了,今天的分享就到这👀。大家能否感受到通过按【模块阅读】这种方式来阅读源码的乐趣呢

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

相关推荐
子兮曰9 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰9 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝9 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码9 小时前
别再前后端各写一套表单校验了
java·后端
大勇前进10 小时前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu10 小时前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile10 小时前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白8010 小时前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙10 小时前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发11 小时前
软件工程SOLID 五大设计原则
后端·软件工程