LoginSignup
2
4

More than 5 years have passed since last update.

Spring Integrationのhttp inbound-gatewayにHandlerInterceptorを適用する方法

Posted at

今お仕事でSpring Integrationと戯れているのですが、Http.inboundGateway(Java DSL)または<http-int:inbound-gateway>(XMLネームスペース)を使ってBean定義したHandler(HttpRequestHandlingMessagingGateway)に対してSpring MVCのHandlerInterceptorが適用できなくてちょっとだけ困っていたのですが、IntegrationRequestMappingHandlerMappingのソースを追ったら適用する方法がわかりました。

Spring Integrationでは、

JavaDSLを使用したHandlerのBean定義例
@Bean
public IntegrationFlow greetingInboundGatewayFlow(MessageChannel httpInboundChannel) {
  return IntegrationFlows.from(Http.inboundGateway("/greeting")
      .requestMapping(mapping -> mapping.methods(HttpMethod.GET))
      .requestChannel(httpInboundChannel)
  ).get();
}
XMLネームスペースを使用したHandlerのBean定義例
<http-int:inbound-gateway
    path="/greeting" supported-methods="GET"
    request-channel="httpRequestChannel"/>

といったBean定義を行うことで、

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/greeting")
@RestController
public class GreetingRestController {
  @GetMapping
  public String greeting() {
    // ...
    return responseMessage;
  }
}

というControllerを作成した時と同じエンドポイントを公開することができます。

通常のSpring MVC(@RequestMappingベースのHandler)だと・・・

Controllerクラスに@RequestMappingメソッドを実装するスタイルであれば、以下のようなBean定義をすることでHandlerInterceptorを適用することができます。

JavaConfigによるBean定義例
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MessageLoggingHandlerInterceptor())
                .addPathPatterns("/**") // 適用対象のパス(パターン)を指定する
                .excludePathPatterns("/static/**"); // 除外するパス(パターン)を指定する
    }
}
XMLによるBean定義例
<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/static/**"/>
    <bean class="com.example.MessageLoggingHandlerInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

Spring Integrationのhttp inbound-gatewayだと・・・

Spring Integrationのhttp inbound-gatewayを使用して定義したHandlerは、通常のSpring MVCとは別にリクエストマッピングが管理されます。具体的にはIntegrationRequestMappingHandlerMappingというクラスから作られたBeanの中でリクエストマッピングが管理され、HandlerInterceptorIntegrationRequestMappingHandlerMappingのBeanの中で管理されているものが適用される仕組みになっています。

Spring Integrationのhttp inbound-gatewayにHandlerInterceptorを適用するには・・・

IntegrationRequestMappingHandlerMapping(正確にはIntegrationRequestMappingHandlerMappingの親クラスであるAbstractHandlerMapping)には、DIコンテナからHandlerInterceptor(正確にはHandlerInterceptorと「適用パス」「除外パス」を保持するMappedInterceptor)を自動で検出してくれる仕組みが備わっているため、HandlerInterceptorのBean定義をすればよいだけでした。

JavaConfigによるBean定義例
@Bean
public MappedInterceptor customHandlerInterceptor() {
  return new MappedInterceptor(
      new String[]{"/**"},
      new String[]{"/static/**"},
      new MessageLoggingHandlerInterceptor());
}
XMLによるBean定義例
<bean class="org.springframework.web.servlet.handler.MappedInterceptor">
  <constructor-arg name="includePatterns" value="/**"/>
  <constructor-arg name="excludePatterns" value="/static/**"/>
  <constructor-arg name="interceptor">
    <bean class="com.example.MessageLoggingHandlerInterceptor"/>
  </constructor-arg>
</bean>

ただし・・・HanderInterceptorを複数定義し、かつ適用順序を制御する必要がある場合は、Bean定義順=適用順になる保証はないため、IntegrationRequestMappingHandlerMappingのBeanを定義して明示的にHanderInterceptorを指定する必要があります。

JavaConfigによるBean定義
@Bean
public IntegrationRequestMappingHandlerMapping integrationRequestMappingHandlerMapping() { // Bean名はintegrationRequestMappingHandlerMappingにする必要あり
  IntegrationRequestMappingHandlerMapping mapping = new IntegrationRequestMappingHandlerMapping();
  mapping.setOrder(0); // orderは0にする必要あり
  mapping.setInterceptors( // MappedInterceptorを適用したい順で追加する
      new MappedInterceptor(new String[] {"/**"}, new String[] {"/static/**"},
          new CustomHandlerInterceptor()),
      new MappedInterceptor(new String[] {"/**"}, new String[] {"/static/**"},
          new MessageLoggingHandlerInterceptor()));
  return mapping;
}
XMLによるBean定義例
<bean id="integrationRequestMappingHandlerMapping"
      class="org.springframework.integration.http.inbound.IntegrationRequestMappingHandlerMapping">
  <property name="order" value="0"/>
  <property name="interceptors">
    <array>
      <bean class="org.springframework.web.servlet.handler.MappedInterceptor">
        <constructor-arg name="includePatterns" value="/**"/>
        <constructor-arg name="excludePatterns" value="/static/**"/>
        <constructor-arg name="interceptor">
          <bean class="com.example.CustomHandlerInterceptor"/>
        </constructor-arg>
      </bean>
      <bean class="org.springframework.web.servlet.handler.MappedInterceptor">
        <constructor-arg name="includePatterns" value="/**"/>
        <constructor-arg name="excludePatterns" value="/static/**"/>
        <constructor-arg name="interceptor">
          <bean class="com.example.MessageLoggingHandlerInterceptor"/>
        </constructor-arg>
      </bean>
    </array>
  </property>
</bean>

まとめ

HTTP向けのエンドポイント公開するなら普通にController作ればいいじゃん!!という話はありますが、今携わっている案件ではHTTP以外のプロトコルもサポートする必要があるため、Spring Integrationを使ってシステム間連携のアーキテクチャを合わせようとしています。
基本的にはSpring Integrationの世界の中で共通処理なども行おうと思っていますが、いくつかの処理(通信ログ出力など)はSpring Integrationの世界に入る前に行う必要がでてきそうなので、HandlerInterceptor使えないか試してみたという感じです。

2
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4