Programing/Framework
[Spring MVC] HandlerMethodArgumentResolver 구현하기
나모찾기
2020. 9. 18. 14:32
가끔 Controller 에 특정 타입의 값을 자동으로 받고 싶을 경우가 있다.
path가 "foo/{a}/{b}" 인데 a 랑 b값을 aggregation 해서 특정 객체로 만들고 싶은데 프레임워크에게 시키고 싶은 것이다.
이럴 경우 @PathVariable 에 대한 메서드 인자 Resolver 를 만들어주면 해결이 가능하다.
기본적으로 @PathVariable Resolver는 두 가지가 있다.
PathVariableMethodArgumentResolver
PathVariableMapMethodArgumentResolver
위의 경우는 파라미터의 이름이나 @PathVariable 에 지정된 value 의 값을 매핑시켜준다.
가끔 타입 변환이 필요한 경우에는 conversionService에게 타입 변환을 요청한다.
PathVariableMapMethodArgumentResolver 의 경우 Map의 타입으로 변환을 한다.
스프링이 기본적으로 추가해주는 ArgumentResolver 목록
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
// ..
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
return this.customArgumentResolvers;
}
위의 목록들의 계층구조은 아래와 같다.
Resolver | 관련 annotaion | 용도 |
HandlerMethodArgumentResolver | <인터페이스> | |
AbstractNamedValueMethodArgumentResolver | <추상 클래스> | |
- ServletCookieValueMethodArgumentResolver | 쿠키 값을 받을 수 있게 해줌 | |
- SessionAttributeMethodArgumentResolver | @SessionAttribute | NativeWebRequest의 RequestAttributes.SCOPE_SESSION 속성 값을 획득 |
- ExpressionValueMethodArgumentResolver | @Value | |
- MatrixVariableMethodArgumentResolver | @MatrixVariable | |
- RequestAttributeMethodArgumentResolver | @RequestAttribute | |
- RequestHeaderMethodArgumentResolver | @RequestHeader | |
- RequestParamMethodArgumentResolver | @RequestParam, |
|
- PathVariableMethodArgumentResolver | @PathVariable | |
ErrorsMethodArgumentResolver | Errors.class | |
HttpEntityMethodProcessor | HttpEntity.class || RequestEntity.class | |
MapMethodProcessor | Map.class | |
MatrixVariableMapMethodArgumentResolver | @MatrixVariable | |
ModelMethodProcessor | Model.class | |
PathVariableMapMethodArgumentResolver | @PathVariable | |
RedirectAttributesMethodArgumentResolver | RedirectAttributes.class | |
RequestHeaderMapMethodArgumentResolver | @RequestHeader + Map.class | |
RequestParamMapMethodArgumentResolver | @RequestParam | |
RequestParamMethodArgumentResolver | @RequestParam | |
RequestPartMethodArgumentResolver | @RequestPart, |
|
RequestResponseBodyMethodProcessor | @RequestBody | |
ModelAttributeMethodProcessor | @ModelAttribute, @ConstructorProperties | 임의의 객체(Model)을 Contoller의 메서드에 사용시 |
ServletModelAttributeMethodProcessor | ModelAttributeMethodProcessor 의 서블릿 한정 사용되는 처리기 URI 템플릿 변수의 모델 속성을 초기화하는 fall-back 전략으로도 사용한다. |
|
ServletRequestMethodArgumentResolver | WebRequest.class || ServletRequest.class || MultipartRequest.class || HttpSession.class || Principal.class || InputStream.class || Reader.class || HttpMethod.class || Locale.class || TimeZone.class || java.time.ZoneId |
HttpServletRequest ... |
ServletResponseMethodArgumentResolver | ServletResponse.class || OutputStream.class || Writer.class | ServletResponse ... |
SessionStatusMethodArgumentResolver | SessionStatus.class | |
UriComponentsBuilderMethodArgumentResolver | UriComponentsBuilder.class or ServletUriComponentsBuilder.class |
annotation 확인을 위해서는 parameter.hasParameterAnnotation(annotation.class) 으로
class 타입의 확인은 parameter.getParameterType() (Class<?> type) 으로
타입의 이름은 paramType.getName() 를 통해 하고 있다.