[Spring] HandlerMethodArgumentResolver 동작 원리
0. 들어가기 전에
ArugmentResolver
을 사용해보고, 어느 시점에서 사용 되는지 알아보기 위해 정리했습니다.
1. HandlerMethodArgumentResolver란?
특정 조건에 맞는 파라미터가 있을 때 원하는 값을 바인딩해주는 인터페이스
컨트롤러에 있는 메서드 파라미터는 누가 넘겨주는 것일까? 우리는 @GetMapping
, @PostMapping
등이 붙은 메서드를 사용하면서, 메서드 파라미터 값에 @RequestParam
, @RequestBody
를 사용해서 값을 입력받는다. JSON을 파싱하고 파라미터에 값을 넣어주는 복잡한 연산들을 누가하는 것일까? 이 연산들을 하는 것은 흔히 얘기하는 ArgumentResolver
즉, HandlerMethodArgumentResolver
가 한다.
HandlerMethodArgumentResolver
는 영어 그대로 해석하자면 핸들러에 있는 메서드의 인자 확인자
이다. 핸들러는 컨트롤러이고, 메서드 인자는 메서드 파라미터이므로 풀어서 말하자면 컨트롤러에 있는 메서드 파라미터를 결정해주는 역할
을 하는 인터페이스가 된다.
우리가 자주 사용하는 @RequestParam
, @RequestBody
모두 ArgumentResolver
가 사용하는 어노테이션으로 각각 HandlerMethodArgumentResolver
를 구현한 구현체들이 어노테이션에 맞게 파라미터 바인딩을 해준다.
2. 동작 위치
그림으로 설명하기 위해 지난번에 포스팅했던 DispatcherServlet 글의 동작과정 그림을 가져와 봤다.
DispatcherServlet의 동작을 더 집중하기 위해 ArgumentResolver의 동작을 생략했지만, 실제로 ArgumentResolver는 4번 동작 쯤에서 작동한다.
아래는 좀 더 자세히 나타낸 그림이다.
[순서]
- 컨트롤러를 처리하기 전에 어댑터는
ArgumentResolver
에 요청을 해 컨트롤러에서 필요한 파라미터를 요청한다. ArgumentResolver
는Controller
를 보고 파라미터를 찾는다.- 핸들러 어댑터에게 파라미터를 반환한다.
- 핸들러 어댑터는 파라미터를 가지고 컨트롤러를 처리한다.
- 컨트롤러를 처리하고 결과 값을
HandlerMethodReturnValueHandler
인터페이스를 걸쳐서 처리한다.(ex)@ResponseBody
,String
,ModelAndView
) - -> 여기서
HandlerMethodReturnValueHandler
를 통해서 반환하는 이유는 다양한 타입을 처리하기 위해서이다.
실제로 @RequestParam
은 RequestParamMethodArgumentResolver
가 동작하고, @ReqeustBody
는 RequestResponseBodyMethodProcessor
라는 HandlerArgumentResolver
의 구현체들이 처리를 하게 된다.
이제 코드로 확인을 해보자.
3. 동작 과정
처음에 DispatcherServlet
이 컨트롤러에서 @RequestMapping
이 붙은 메서드의 정보를 보고, RequestMappingHandlerAdapter
를 찾는다. 그 후 InvocableHandlerMethod
를 실행해서 ArgumentResolver
를 선택해 처리한다.
// InvocableHandlerMethod#invokeForRequest
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); // 여기를 좀 더 자세히 살펴보자.
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
// InvoableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// ...
// 메서드 파라미터의 정보를 저장하는 클래스
MethodParameter[] parameters = getMethodParameters();
// ...
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
// resolver가 여기서 작동하게 됨
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
// ...
}
return args;
}
this.resolvers
은 HandlerMethodArgumentResolver
의 구현체인 HandlerMethodArgumentResolverComposite
로 되어 있다. 이 구현체에는 스프링에서 등록한 모든 ArgumentResolver를 들고있고, for문을 돌며 supportParameter()
를 통해 적용할 수 있는 resolver를 선택해 처리하게 된다.
※ 참고
'FRAMEWORK > [SPRING]' 카테고리의 다른 글
LazyConnectionDataSourceProxy의 역할 (0) | 2024.01.26 |
---|---|
[Spring] Spring Rest docs 적용해보기 (1) | 2023.05.21 |
[Spring] DispatcherServlet이란? (0) | 2023.04.27 |
[Spring] Spring에서 Bean은 어떤 자료구조로 관리될까요? (0) | 2023.04.22 |
[Spring] Spring에서 DI하는 3가지 방법 (1) | 2023.04.21 |