개발 기록
> [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (1) 본문

환경
java 17
spring version 6.0.11
spring boot version 3.1.3

■
1. 요청 처리 간략한 흐름
스프링은 서블릿에 도착한 모든 요청에 대해 다음과 같은 전처리를 진행한다.
1) DispatcherServlet : 요청 URI를 받음
2) HandlerMapping : 요청을 분석하여 해당 요청 URI 에 맞는 핸들러매핑 결정
3) HandlerAdapter : HandlerMapping를 통해 모든 핸들러를 조회한 뒤 적절한 핸들러를 호출
3-1) Argumenet Resolver 처리
3-2) Message Convert 처리
3-3) Controller Method Invoke
3-4) 핸들러 적용
■
2. RequestMappingHandlerAdapter
@RequestMapping, @GetMapping, @Postmapping 등 애노테이션 기반의 HandlerMethod(controller) 처리
① ArgumentResolver를 호출해서 HandlerMethod(controller) 가 필요로 하는 다양한 파라미터의 값(객체)을 생성
② 파라미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.
(1) RequsetMappingHandlerAdapter 에서 선언된 argumentResolver

▶ customArgumentResolvers
: getCustomArgumentResolvers(): 사용자 정의 argument or null.
▶ argumentResolvers
: getArgumentResolvers() : Return the configured argument resolvers or null(아직 초기화되지 않은 경우)
▶ initBinderArgumentResolvers
getInitBinderArgumentResolvers () : Return the argument resolvers for @InitBinder or null (아직 초기화되지 않은 경우)
■
3. RequestMappingHandlerAdapter 주요 메서드 -> InvocableHadlerMethod()
@RequestMapping 을 처리하는 메서드를 호출한다.
(1) invokeHandlerMethod()
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// WebDataBinderFactory
// : 요청 핸들러 메서드의 Parameter를 Binding할 데이터 바인더를 생성하는 팩토리
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
...
// 핸들러(컨트롤러)를 실행
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
// 컨트롤러 결과에 따라 ModelAndView 를 만들어서 반환
return asyncManager.isConcurrentHandlingStarted() ? null : this.getModelAndView(mavContainer, modelFactory, webRequest);
}
invokeHandlerMethod 내부에서는 'ServletInvocableHandlerMethod ' 의 invokeAndHandle을 호출한다.
invokeAndHandle 메서드에서는 핵심은 invokeForRequest 및 'HandlerMethodReturnValueHandlerComposite'의 handleReturnValue 의 호출이다.
(2) invoketForRequest()
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainerm, mavContainer, Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); // 2-1
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args); // 2-2
}
2-1 . geMethodArgumentValues
getMethodArgumentValues 내부를 살펴보면 resolverArgument 메서드의 return 값을 args 배열에 값을 추가하는 것을 볼 수 있다.

resolverArgument 메서드가 어떤 값을 반환하는지 살펴보자!

이 resolverArgument() 메서드는
HandlerMethodArgumentResolverComposite class 가 HandlerMethodArgumentResolver 의 메서드를 구현 한 것이다.
해당 메서드는 다른 모든 HandlerMethodArgumentResolver를 상속받은 객체를 가지고 루프를 돌며 (getArgumentResolver())
주어진 메소드 매개변수를 지원하는 HandlerMethodArgumentResolver를 찾고 해당 핸들러 메서드의 매개변수 값을 리턴한다.(resovler.resolverArgument())
그 다음 invokeForReques 메서드로 돌아와서 핸들러 메서드(=controller)의 매개변수 값이 담긴 args 배열을 인자값으로 하여 doInvoke() 메서드를 실행한다.
2-2 . doInvoke() : 핸들러 메서드 실제 실행
☞ this.getBridgeMethod() : HadlerMethod 의 getBridgedMethod() 를 호출하여 브리지된(사용자 정의) 메서드를 반환한다.
☞ method.invoke() : 해당 method 를 invoke(실행) 한다.

* 만약 아래와 같은 핸들러 메서드가 있다고 하면 home() 메서드가 bridgedMethod 가 되는 것이다.

이후 invokeForRequest 호출이 반환되고 리턴타입을 지원하는 핸들러를 찾아서 해당 핸들러를 적용하여 결과를 처리한다.
■
4. 정리

★ 관련 Object
| RequestMappingHandlerAdapter | @RequestMapping을 처리하는 핸들러 어댑터. ArgumentResolver 호출다. |
| HandlerMethodArgumentResolver | 요청값을 핸들러 메서드 인자값으로 바인딩하여 전달 |
| HandlerMethodReturnValueHandler | 응답 값을 변환하고 처리한다. (ex. view, responsebody, httpEntity 반환) |
★ Request value 위치에 따른 ArgumentResolver
| RequestResponseBodyMethodProcessor | @RequestBody, @ResponseBody |
| HttpEntityMethodProcessor | HttpEntity |
| AbstractNamedValueMethodArgumentResolver | @RequestParam, @Pathvariable |
★ Response
| HandlerMethodReturnValueHandler | HTTP Message Converter를 호출해서 응답 결과를 만든다 |
🚩 @ResponseBody 의 경우
RequestResponseBodyMethodProcessor.handleReturnValue()
: 모델에 속성을 추가하고 보기를 설정하거나 응답이 직접 처리되었음을 나타는 ModelAndViewContainer.setRequestHandled(boolean)플래그를 설정하여 지정된 반환 값을 처리합니다.
매개변수:
-returnValue- 핸들러 메소드에서 반환된 값
-returnType- 반환 값의 유형입니다.
-mavContainer- 현재 요청에 대한 ModelAndViewContainer
-webRequest- 현재 요청
참고
https://kmkunk.tistory.com/149
https://namocom.tistory.com/824
'DIVE' 카테고리의 다른 글
| > 캐싱에 대해 알아보자! 캐시는 언제 써야 할까? (0) | 2024.01.05 |
|---|---|
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (5) HttpMessageConvert (0) | 2023.12.12 |
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (4) Convert (0) | 2023.12.08 |
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (3) Request, Response Log (0) | 2023.12.08 |
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (2) HandlerMethodArgumentResolver : Request Value Binding (0) | 2023.12.07 |