asoview! developers blog

ワクワクをすべての人に。

HandlerInterceptorAdapterで共通処理を行う

こんにちは、@seitです。 Spring Bootで、各Controllerの共通処理を実装する機会があったので、備忘録です。

サンプルはこちらからどうぞ。

やりたいこと

HTTPリクエストに対して、どこにアクセスされたのかをログに出したい。
全てのリクエストハンドラ(Controller)に処理を入れるのは、実装の工数や保守性の観点でやりたくないので共通化したい。

利用するAPI

今回はHandlerInterceptorAdapterを使います。
Spring Bootで共通処理を挟み込む方法はいくつかあります。

名前 概要
AdviceController Controller専用の特殊なメソッド(initBinderやExceptionHandler、ModelAttribute)を複数のControllerで共有することが可能
サーブレットフィルター DispatcherServletの呼び出し前後に共通処理を挟み込むことが可能
HandlerInterceptor DispatcherServletとリクエストハンドラとの間に共通処理を挟み込むことが可能。Controllerに対してだけ共通処理を実行したい場合に利用
AOP アスペクト指向プログラミング

今回は、HTTPリクエストでハンドラが呼ばれる前に、どのコールされたハンドラを記録するログを出力したいと思います。
この場合、以下の2つの理由から、HandlerInterceptorが適していると思いました。

  • リクエストを受け取るControllerに対してだけ共通処理を実装すればよい

  • ハンドラの関数名をログに出力したいので、DispatcherServletとControllerの間に処理を挟みたい

  • initBinderやModelAttributeはログを出すだけという目的と用途が合致しない

  • AOPはそこまでする必要はなさそう

実装

まずは、HandlerInterceptorを継承したクラスを作成し、Overrideしたメソッドに共通にしたい処理を実装します。
今回は、HTTPリクエストの内容をいくつか取得してログに出力してみます。

public class LoggingInterceptor extends HandlerInterceptorAdapter {

    Logger logger = org.slf4j.LoggerFactory.getLogger(getClass().getPackage().getName());

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String requestUri = request.getRequestURI();
        String requestMethod= request.getMethod();
        String referer = request.getHeader("Referer");
        HandlerMethod hm = (HandlerMethod) handler;
        String handlerClassName = hm.getMethod().getDeclaringClass().getSimpleName();
        String handlerMethodName = hm.getMethod().getName();

        String logInfo = new StringBuffer()
                .append(requestUri)
                .append(", method ")
                .append(requestMethod)
                .append(", from ")
                .append(referer)
                .append(", handlerClassName")
                .append(handlerClassName)
                .append(", handlerMethodName")
                .append(handlerMethodName).toString();

        logger.info(logInfo);

        return true;
    }
}

preHandleメソッドは、Controllerが呼ばれる前に実行されます。
Controllerの後に実行したい場合は、postHandleメソッドをOverrideします。

最後に、WebMvcConfigurerAdapterのaddInterceptorsメソッドの中で、
作成したLoggingInterceptorを登録します。

@Configuration
public class SampleConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**");
    }

}

この時、addPathPatternsでLoggingInterceptorを有効にしたいURIのパターンを指定することができます。
ここでは"/"以下全てのURIへのアクセスに対して、ログを出力するように設定しています。

2019-09-29 13:54:36.347  INFO 21072 --- [nio-8080-exec-1] com.example.HandlerInterceptordemo       : /, method GET, from null, handlerClassNameSampleController, handlerMethodNametop
2019-09-29 13:54:36.353  INFO 21072 --- [nio-8080-exec-1] com.example.HandlerInterceptordemo       : これはControllerのログです。

以上です。

www.wantedly.com