LoginSignup
16
14

More than 5 years have passed since last update.

Spring BootでHandlerメソッドのパラメータに任意のクラスを書き、引数にそのインスタンスを受け取る

Posted at

概要

HandlerメソッドのパラメータにLocaleやTimeZoneなどのクラスを書くと自動的にそのインスタンスが渡されますが(下記の例のように)、

@Controller
public class MyController {

  @RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
  public String index(Locale locale, TimeZone timezone) {

    //springがlocale,timezoneにインスタンスをセットしてくれる
    logger.info("locale:{}", locale);
    // locale:ja
    logger.info("timezone:{}", timezone);
    // timezone:sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null]
  }

}

これと同じように任意のクラスをパラメータに書き、そのインスタンスを引数として受け取るにはHandlerMethodArgumentResolverインタフェースを実装したクラスを作成することで可能になります。

環境

  • Windows10 Professional
  • Java 1.8.0_101
  • Spring Boot 1.4.1

参考

サンプルコードの説明

HandlerメソッドがMyCustomModelというクラスのインスタンスを引数に受け取る例です。

パラメータにするクラス

Handlerメソッドのパラメータにするクラスは下記のようにしました。

public class MyCustomModel {

    private String hoge;
    private String fuga;
    private String myHeader;
    private String myCookie;
    private Date mySession;

    // getter/setterは省略

}

コントローラーのHandlerメソッド

引数を受け取るHandlerメソッドのパラメータの指定方法はいくつかありますが、この記事では下記の3種類について説明します。

  • メソッドアノテーションで指定する
  • パラメータアノテーションで指定する
  • パラメータのクラスの型/名前で指定する
MyController
@Controller
public class MyController {

    /*
     * メソッドのアノテーションで指定する
     */
    @MyCustomAnnotation
    @RequestMapping(value = "/piyo", method = RequestMethod.GET)
    public String piyo(MyCustomModel myCustom) {

        // myCustomには自動的にインスタンスがセットされている
        logger.info("{}", myCustom);

        return "my/piyo";
    }

    /*
     * パラメータのアノテーションで指定する
     */
    @RequestMapping(value = "/poyo", method = RequestMethod.GET)
    public String poyo(@MyCustomAnnotation MyCustomModel myCustom) {

        // myCustomには自動的にインスタンスがセットされている
        logger.info("{}", myCustom);

        return "my/poyo";
    }

    /*
     * パラメータの型/名前で指定する
     */
    @RequestMapping(value = "/puyo", method = RequestMethod.GET)
    public String puyo(MyCustomModel myCustom) {

        // myCustomには自動的にインスタンスがセットされている
        logger.info("{}", myCustom);

        return "my/puyo";
    }

}

アノテーション

このサンプルコードで使用するマーカーアノテーションです。

MyCustomAnnotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
public @interface MyCustomAnnotation {
}

HandlerMethodArgumentResolverを実装する

このインターフェースを実装するクラスを用意します。
実装が必要なメソッドはsupportsParameterとresolveArgumentです。

MyCustomHandlerMethodArgumentResolver
public class MyCustomHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public Object resolveArgument(MethodParameter parameter
                                , ModelAndViewContainer container
                                , NativeWebRequest request
                                , WebDataBinderFactory factory) throws Exception {

        // 実装する

    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {

        // 実装する

    }

}

supportsParameterメソッド

このメソッドはHandlerメソッドのパラメータの1つ1つに対して呼ばれます。(ただしSpringが自動的に値をセットするパラメータは除外されます。たとえば@PathVariableアノテーションが付いているパラメータやModel、UriComponentsBuilderなど)
このメソッドがtrueを返した場合に、次にresolveArgumentメソッドが実行されます。

メソッドアノテーションで指定

メソッドにMyCustomAnnotationアノテーションが付いている場合。
MyControllerの例で言うとHandlerメソッドpiyoのMyCustomModelパラメータにインスタンスが渡されます。

MethodParameter#hasMethodAnnotation
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasMethodAnnotation(MyCustomAnnotation.class);
}
パラメータアノテーションで指定

パラメータにMyCustomAnnotationアノテーションが付いている場合。
MyControllerの例で言うとHandlerメソッドpoyoのMyCustomModelパラメータにインスタンスが渡されます。

MethodParameter#hasParameterAnnotation
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(MyCustomAnnotation.class);
}
パラメータの型/名前で指定

アノテーションを使わなくても、パラメータの型又は名前で指定することができます。
MyControllerの例で言うとすべてのHandlerメソッドのMyCustomModelパラメータにインスタンスが渡されます。

パラメータの型がMyCustomModelの場合。

MethodParameter#getParameterType
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.getParameterType().equals(MyCustomModel.class);
}

パラメータの名前が"myCustom"の場合。
ただし、そのパラメータの型がMyCustomModelではない場合は例外がスローされます。

MethodParameter#getParameterName
@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.getParameterName().equals("myCustom");
}

resolveArgumentメソッド

supportsParameterメソッドがtrueを返した場合に実行され、このメソッドが返すオブジェクトが引数として使用されます。

resolveArgument
@Override
public Object resolveArgument(MethodParameter parameter
                            , ModelAndViewContainer container
                            , NativeWebRequest request
                            , WebDataBinderFactory factory) throws Exception {

    MyCustomModel myCustom = new MyCustomModel();

    String hoge = request.getParameter("hoge");
    String fuga = request.getParameter("fuga");
    myCustom.setHoge(hoge);
    myCustom.setFuga(fuga);

    String myHeader = request.getHeader("User-Agent");
    myCustom.setMyHeader(myHeader);

    return myCustom;
}
NativeWebRequest#getNativeRequest

HttpServletRequestクラスのインスタンスを取得することができます。

HttpServletRequest httpServletRequest = request.getNativeRequest(HttpServletRequest.class);

// Cookieを扱うことができます。
Cookie myCookie = WebUtils.getCookie(httpServletRequest, "myCookie");
if (myCookie != null) {
    myCustom.setMyCookie(myCookie.getValue());
}

// Sessionを扱うことができます。
HttpSession session = httpServletRequest.getSession(false);
if (session != null && session.getAttribute("mySession") != null) {
    Date mySession = (Date) session.getAttribute("mySession");
    myCustom.setMySession(mySession);
}
ModelAndViewContainer#addAttribute

Modelにインスタンスをセットして直接ビュー(例えばthymeleaf)へ渡すことができます。

MyCustomModel myCustom = new MyCustomModel();

// myCustomにデータを詰めるコード

container.addAttribute("myCustom", myCustom);

WebMvcConfigurerAdapterを継承したクラスを用意する

作成したMyCustomHandlerMethodArgumentResolverを有効にするには、addArgumentResolversメソッドで追加します。

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.example.web.aws.resolver.MyCustomHandlerMethodArgumentResolver;

@Configuration
public class MyWebMvcConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new MyCustomHandlerMethodArgumentResolver());
    }

}
16
14
0

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
16
14