개발 기록

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

DIVE

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

1z 2023. 12. 7. 16:53

 

 

 

 

 

 

 

환경

java 17
spring version 6.0.11 
spring boot version 3.1.3

 

https://vkuzel.com/request-mapping-on-demand-in-spring-mvc

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 배열에 값을 추가하는 것을 볼 수 있다. 

getMethodArgumentValues ()

 

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

HandlerMethodArgumentResolverComposite class

 

이 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