Spring Messaging でのWebSocketメモ

Spring MessagingでのSTOMPは以下の形で設定を行う。 これがなぜ動くのかを軽く探ってみたのでメモとして残しておく。

普通にSpringでWebsocketを使いたいならリファレンスのここ見ればよいかと。

なぜこのリファレンスにあるソースが動くのかを大まかに調べるためにこの記事にメモしておく。

import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/portfolio").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/topic", "/queue");
    }

}

アノテーション@EnableWebSocketMessageBrokeには以下のアノテーションが付けられている

...
@Import({
  DelegatingWebSocketMessageBrokerConfiguration.class
})
public @interface EnableWebSocketMessageBroker {
}

DelegatingWebSocketMessageBrokerConfigurationが渡されている。

DelegatingWebSocketMessageBrokerConfigurationの中身

InjectionされたWebScoketMessageBrokerConfigurerのリストに対して 処理を移譲する形になっている。

親クラスWebSocketMessageBrokerConfigurationSupportにて
SimpAnnotationMethodMessageHandlerを返却するcreateAnnotationMethodMessageHandlerが実装されている。
このメソッド内部ではWebSocketAnnotationMethodMessageHandlerインスタンス化されて返却されている。
このメソッドはAbstractMessageBrokerConfigurationのsimpAnnotationMethodMessageHandlerから呼び出される。

WebSocketAnnotationMethodMessageHandlerSimpAnnotationMethodMessageHandlerの子クラスである。
SimpAnnotationMethodMessageHandlerにはinitReturnValueHandlersというメソッドが実装されており
これはこの親クラスのAbstractMethodMessageHandlerにて抽象メソッドとして定義されている。

また、このクラスAbstractMethodMessageHandlerから afterPropertiesSetメソッドにてinitReturnValueHandlersが呼び出されている。
afterPropertySetメソッドはInitializingBeanで定義されているメソッドである。

話を元に戻して、先ほどのSimpAnnotationMethodMessageHandlerのinitReturnValueHandlersではSendToMethodReturnValueHandlerSubscriptionMethodReturnValueHandler
その他のHandlerMethodReturnValueHandlerを実装しているクラスのリストを返却している。

HandlerMethodReturnValueHandlerは以下のようなインターフェースになっている。

package org.springframework.messaging.handler.invocation;

import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;

public interface HandlerMethodReturnValueHandler {
  boolean supportsReturnType(MethodParameter arg0);

  void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> message) throws Exception;
}

この実装の参考はSendToMethodReturnValueHandlerを見るとよいかと思われる。

HandlerMethodReturnValueHandlerのhandleReturnValueが呼ばれる個所

AbstractMethodMessageHandlerafterPropertiesSetにてinitReturnValueHandlersが呼ばれたのちHandlerMethodReturnValueHandlerCompositeのaddHandlersメソッドにそのまま渡されている
このクラスはHandlerMethodReturnValueHandlerを実装しており
実際にはaddHandlersで追加されたHandlerに処理を移譲するようなクラスになっている。

HandlerMethodReturnValueHandlerCompositegetReturnValueHandlerにてaddHandlersで追加されたHandlerのsupportsReturnTypeの戻り値を見て
実際に使うMethodHandlerの決定が行われる。

このメソッドは、 AbstractMethodMessageHandler#handleMessage (MessageHandler#handleMessage)
AbstractMethodMessageHandler#handleMessageInternal
AbstractMethodMessageHandler#handleMatch
HandlerMethodReturnValueHandlerComposite#handleReturnValue
HandlerMethodReturnValueHandlerComposite#getReturnValueHandler
の流れで呼ばれる。

Websocket Scopeの実装

Spring MessagingとSpring Websocketの連携では、websocket scopedなBeanの定義が可能になっている。 これらのScopeの実装に関わるクラスは以下のクラスである。

  1. org.springframework.messaging.simp.SimpSessionScope
  2. org.springframework.messaging.simp.SimpAttributesContextHolder
  3. org.springframework.messaging.simp.SimpAttributes

org.springframework.messaging.simp.SimpAttributesContextHolderの内部にて
ThreadLocalな値を保持しており、その値の型はSimpAttributes型になっている。

SimpAttributesはただのMapである。
解説はここでは特にしない。

また、SimpAnnotationMethodMessageHandlerのhandleMatchにてSimpAttributesContextHolderのsetAttributesFromMessageとresetAttributesが呼び出されており
ここでWebsocket Scopeの実装が行われているように伺える。

まとめ

とりあえずこれまでのSpringはここまで。