HandlerMethodArgumentResolver 와 HandlerMethodReturnValueHandler의 동작에 대해서 알아야하는데, 이를 알아보기전에 스프링 부트에서 http 요청이 어떻게 처리되는지 우선 알아야한다.
dispatcher servlet
http 요청이 들어오면 dipatcher servlet의 doDispatch 메서드를 거치게 된다.
doDispatch 메서드를 간소화하자면,
- getHandler 를 통해서, 요청을 처리할 수 있는 Handler 를 찾는다.
- handler 를 처리할 수 있는 handlerAdapter 를 찾는다.
- handlerAdapter를 통해서 실제 handler 를 호출한다.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
RequestMappingHandlerAdapter
@RequestMapping 또는 @RestController 어노테이션이 이 붙은 컨트롤러의 경우,
기본적으로 RequestMappingHandlerAdapter 를 통해서 처리가 된다.
해당 어댑터의 코드에서 invokeHandlerMethod 메서드를 간소화해서 보면,
- invocableMethod 에 arugment resolver, retunValue handler를 등록한다.
- 그리고 invocableMethod 를 호출한다.
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
HandlerMethodArgumentResolver
위에서 등록된, invocableMethod 가 호출될 때 메서드의 argument에 대해서 argument resolver 를 수행하게 된다.
- argument (parameter) 를 loop 를 돌면서 resolver 호출 결과를 반환한다.
- 이렇게 resolver 가 적용된 arguments를 반환하게 된다.
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++) {
MethodParameter parameter = parameters[i];
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
}
return args;
}
returnValue handler의 경우에도 마찬가지로 동작한다.
@ResponseBody, @RequestBody 도 동일하게 동작한다.
@RequestBody 는 argument resolver를 통해 동작하고,
@ResponseBody 는 returnValue handler 를 통해 동작한다.
이 둘은 RequestResponseBodyMethodProcessor 라는 이름으로 동작한다.
method의 parameter type, return type 의 어노테이션을 보고 적용 여부를 판단하고,
parameter나 return value에 converter를 통해서 값을 변환하여 쓴다.
// RequestResponseBodyMethodProcessor
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
내부적으로는 http message converter 들을 통해서, byte, string, jackson 처리를 모두 가능하게 한다.
요약
spring boot는 request에 대해서 request adapter 에 위임하여, handler 수행을 한다.
request adapter 는, handler 가 어떻게 처리할지에 대한 책임을 가진 객체로, argument와 return value를 resolve 하는 책임을 가지고 있다.
@RequestBody, @ResponseBody 도 이런 resolver 를 통해 처리된다.
'스프링부트' 카테고리의 다른 글
나는 테스트에서 @Transactional 붙이지 않겠다.. (feat. 동시성 삽질기) (0) | 2024.05.02 |
---|---|
jackson for kotlin part3. 삽질기 (feat. jacksonObjectMapper) (0) | 2024.03.10 |
jackson for kotlin part2. 커스터마이징 (0) | 2024.03.10 |
영속성 컨텍스트와 트랜잭션의 관계 (1) | 2024.02.18 |
jackson for kotlin part1. 동작 원리 (1) | 2023.12.30 |