SpringBootでWebSocketを使ってみたい(Sample Backend編)
- はじめに
- Keywords
- クラス構成
- GreetingController
- WebSocketConfig
- AbstractWebSocketMessageBrokerConfigurer
- おわりに
- 参照
はじめに
チャットのように、Webサーバーを通してデータの受け渡しをリアルタイムで実施するための技術にWebSocketがあります。
今回はこれをSpringBootから利用する方法として紹介されていた下記の内容について調べつつ、
いじってみたりすることにします。
Keywords
WebSocket
まずWebSocketについてですが、超乱雑にまとめると
- 最初にクライアント側とサーバー側を連携し、その後は接続・切断をせず小さいオーバーヘッドでやり取りを行う
- クライアント側からだけでなくサーバー側からもデータ(メッセージ)のやりとりをリクエストできる
といったところでしょうか。
頻繁なメッセージのやりとりが必要なチャットなどを作るときに利用されます。
詳しくは下記のようなページをご覧ください。(内容が間違っていたり、あまりにも説明が不足していることがわかれば後日修正します)
- Windows 8 のネットワーク接続 - Windows 8 と WebSocket プロトコル - MSDN
- 26. WebSocket Support - Spring
- RFC 6455 - The WebSocket Protocol (日本語訳)
- WebSocketについて調べてみた。 - Nao Minami’s Blog
今回のサンプルではsockjsを利用しています。
STOMP
WebSocketでクライアント側とサーバー側でやりとりをするためには、
お互いに共通した方法(プロトコル)を持っている必要があります。
サンプルでは、このためのプロトコルとしてSTOMP(Simple (or Streaming) Text Orientated Messaging Protocol)を使用しています。
正式名称の通り、テキストベースのメッセージでやりとりを行います。
メッセージのやりとりの流れは最初にSubscribeを行い、
更新があればそれを受け取る、ということのようです。
このSTOMPをWebSocket上で使用するため、STOMP.jsを利用しています。
一点気がかりなのは、GitHubを見るとメンテナンスが止まってしまっていること。
みなさんどう対処しているのか(´・ω・`)
。。。とりあえず今回はStomp.jsを使用することとします。
クラス構成
上記のサンプルに含まれるクラスは以下の通りです。
- src
- main.java.hello
この内「Application.java」はSpringBootの開始を担うメインクラス、「Greeting.java」「HelloMessage.java」はデータを保持するモデルクラスです。
ここからは「GreetingController.java」「WebSocketConfig.java」について追いかけてみます。
GreetingController
コントローラークラスです。
GreetingController.java
import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class GreetingController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { // 1秒待ってから応答する. Thread.sleep(1000); // メッセージ内容を"/topic/greetings"のSubscriberに渡す. return new Greeting("Hello, " + message.getName() + "!"); } }
- @Controllerアノテーションを付与することで、通常のルーティング処理に加えてメッセージのルーティングも行うことができます。
- @MessageMappingアノテーションを付与することで、今回は「/hello」にメッセージが送信されたときに関数「greeting(HelloMessage message)」が呼ばれます。
このときメッセージの内容は、「HelloMessage.java」に格納された形で引数として渡されます。 - @SendToアノテーションを付与することで、戻り値であるメッセージの内容を「/topic/greetings」をSubscribeしているユーザーに伝えることができます。
- 「Thread.sleep(1000);」は無くても動作します。
なおマッピングできるパスの形式は、「/hello」の他「/hello.message.*」や「/hello.message.{goodmorning}」といったものも指定できるようです(MessageMapping.classより)。
WebSocketConfig
設定クラスです。
WebSocketConfig.java
import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-guide-websocket").withSockJS(); } }
- @EnableWebSocketMessageBrokerアノテーションを付与することで、WebSocketのメッセージのやり取りを有効にします。
ここで実装している内容は2つです。
- MessageBrokerの設定
- STOMPのエンドポイントの登録
1. MessageBrokerの設定
メッセージのやりとりを仲介するMessageBrokerの設定を行います。
config.enableSimpleBroker(“/topic”);
「/topic」以下のURL(GreetingController.javaのメッセージ送信先である「/topic/greetings」)でメッセージを渡せるようにします。
メッセージを受け取ったら、あらかじめSubscribeしていたユーザーにメッセージが渡されます。
config.setApplicationDestinationPrefixes(“/app”);
「/app」以下のURLにメッセージを送ると、コントローラークラス(GreetingController.java)が呼ばれるようになります。
今回はクライアント側から「/app/hello」にメッセージを送信すると、
GreetingController.javaの「greeting(HelloMessage message)」が呼ばれます。
2. STOMPのエンドポイントの登録
sockjsを使ってWebSocketの接続処理を行うエンドポイントの登録を行います。
「withSockJS()」は今回sockjsを使っているため必要なのですが、
sockjsを使わない場合にWebSocketを使う処理が行えるのか、可能であればどのようにするのかは気になるところです。
AbstractWebSocketMessageBrokerConfigurer
設定クラス(WebSocketConfig.java)で継承しているAbstractWebSocketMessageBrokerConfigurerではどのようなことをしているのか、少し見てみることにしました。
AbstractWebSocketMessageBrokerConfigurer.java
package org.springframework.web.socket.config.annotation; import java.util.List; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; public abstract class AbstractWebSocketMessageBrokerConfigurer implements WebSocketMessageBrokerConfigurer { @Override public void configureWebSocketTransport(WebSocketTransportRegistration registration) { } @Override public void configureClientInboundChannel(ChannelRegistration registration) { } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { } @Override public boolean configureMessageConverters(ListmessageConverters) { return true; } @Override public void addArgumentResolvers(List argumentResolvers) { } @Override public void addReturnValueHandlers(List returnValueHandlers) { } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { } }
※コメント、Javadocsは省略しています。
WebSocketMessageBrokerConfigurer.javaのメソッドをOverrideしていますが、
中身はすべて空になっています。
ドキュメントを見ると、WebSocketMessageBrokerConfigurer.javaを実装しやすくするため、
オプショナルな関数を空で実装している抽象クラスのようです。
設定クラスで実装していた「configureMessageBroker(MessageBrokerRegistry registry)」も含まれているため、
実装が必須な関数というのは、STOMPのエンドポイントの登録を行う「registerStompEndpoints(StompEndpointRegistry registry)」だけのようですね。
では、WebSocketMessageBrokerConfigurer.javaも見てみます。
WebSocketMessageBrokerConfigurer.java
package org.springframework.web.socket.config.annotation; import java.util.List; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; public interface WebSocketMessageBrokerConfigurer { void registerStompEndpoints(StompEndpointRegistry registry); void configureWebSocketTransport(WebSocketTransportRegistration registry); void configureClientInboundChannel(ChannelRegistration registration); void configureClientOutboundChannel(ChannelRegistration registration); void addArgumentResolvers(ListargumentResolvers); void addReturnValueHandlers(List returnValueHandlers); boolean configureMessageConverters(List messageConverters); void configureMessageBroker(MessageBrokerRegistry registry); }
※コメント、Javadocsは省略しています。
オプショナルな関数の内容は下記を参照していただきたいところですが、
WebSocketでのメッセージの送受信を行う関数(MessageChannel)の設定などが行えるため、必要に応じてOverrideすることになりそうです。
なお、interfaceで共通の関数を指定して、共通の処理を抽象クラスで実装し、個別の処理を抽象クラスを継承したクラスで実装する、
という流れはまさにJava本格入門で読んだ内容だな〜、などと思いながら見ていました。
おわりに
細かく追ってはみたつもりですが、全体的にまだフワフワしているため、
実際にサンプルを作りながらもう少し突っ込んで触る必要がありそうです。
次回はクライアント側を追いかけてみます。
参照
Spring
- Getting Started · Using WebSocket to build an interactive web application - Spring
- Spring 4.3 WebSocket関連の主な変更点(+簡易アプリ作成!!) - Qiita
- 26. WebSocket Support - Spring
- Spring Bootでチャットツールを作りながらWebの仕組みを理解しよう! - SlideShare
- 6. STOMPを使ってみる — Spring Bootキャンプ ハンズオン資料 1.0.0-SNAPSHOT ドキュメント
WebSocket
- Windows 8 のネットワーク接続 - Windows 8 と WebSocket プロトコル - MSDN
- RFC 6455 - The WebSocket Protocol (日本語訳)
- WebSocketについて調べてみた。 - Nao Minami’s Blog
- SockJS-client - sockjs - GitHub