概要
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
参考
- [22. Web MVC framework / 22.3.3 Defining @RequestMapping handler methods] (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-methods)
- [Spring From the Trenches: Creating a Custom HandlerMethodArgumentResolver] (https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/)
- [Interface HandlerMethodArgumentResolver] (http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/method/support/HandlerMethodArgumentResolver.html)
サンプルコードの説明
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種類について説明します。
- メソッドアノテーションで指定する
- パラメータアノテーションで指定する
- パラメータのクラスの型/名前で指定する
@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";
}
}
アノテーション
このサンプルコードで使用するマーカーアノテーションです。
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です。
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パラメータにインスタンスが渡されます。
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasMethodAnnotation(MyCustomAnnotation.class);
}
パラメータアノテーションで指定
パラメータにMyCustomAnnotationアノテーションが付いている場合。
MyControllerの例で言うとHandlerメソッドpoyoのMyCustomModelパラメータにインスタンスが渡されます。
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(MyCustomAnnotation.class);
}
パラメータの型/名前で指定
アノテーションを使わなくても、パラメータの型又は名前で指定することができます。
MyControllerの例で言うとすべてのHandlerメソッドのMyCustomModelパラメータにインスタンスが渡されます。
パラメータの型がMyCustomModelの場合。
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(MyCustomModel.class);
}
パラメータの名前が"myCustom"の場合。
ただし、そのパラメータの型がMyCustomModelではない場合は例外がスローされます。
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterName().equals("myCustom");
}
resolveArgumentメソッド
supportsParameterメソッドがtrueを返した場合に実行され、このメソッドが返すオブジェクトが引数として使用されます。
@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());
}
}