一、前瞻
Ok,开始我们今天的对Spring的【模块阅读】。
那就挑Web里的WebSocket模块,先思考下本次阅读的阅读线索:
- WebSocket在Spring里起到什么作用
- 这个模块采用了什么设计模式
- 我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁
二、探索
我们先整体看下整个模块的组织机构。
要解决阅读线索3,看起来应该从上图里的server模块开始。
找了一圈发现并没有什么线索,那我们就从阅读官方文档这个方向入手。
官方文档表面心跳采用了SckJS协议,所以我们应该从图2里的sockjs模块开始探索。
既然是发送消息给用户,那必定有send的动作,我们在sockjs模块全局搜索下。
我们根据搜索定位到了发送消息的WebSocketSession接口。
/**
* 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具体实现:
@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发现了以下代码块。
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里起到什么作用,我们可以在官方文档里找到答案。
未完待续。。。
好了,今天的分享就到这👀。大家能否感受到通过按【模块阅读】这种方式来阅读源码的乐趣呢 !
创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️