[Spring] @RequestBody, ResponseEntity 동작과정

klmin·2024년 9월 15일
  • Controller

  • 요청

  • DispatcherServlet

최초에 빈 생성시 기본적으로 설정된 resolver, adapter등을 초기화함.

예를들어 initHandlerMappings 를 보면 DefaultListableBeanFactory 에서 클래스기반으로 빈을 가져오도록 되어있음.

  • doService
    사용자의 요청이들어오면 DispatcherServlet.class에 doService가 호출된다.


doService 내에 doDispatch가 실행됨.

  • doDispatch


getHandler에서 처리할 컨트롤러와 메서드를 찾는다.

기본적으로 6개의 handlermapping이 등록되어있다.

getHandler에서 RequestMappingHandlerMapping이 요청에 대한 컨트롤러와 메서드를 반환한다.

getHaldlerAdapter에서 handleradapter를 찾는다
기본적으로 4가지의 handleradapter가 등록되있다.

supports 를 확인해 adapter를 반환한다.

RequestMappingHandlerAdapter가 반환됨.

interceptor가 있다면 applypreHandle에서 preHandle이 실행됨.
만약 prehandle에서 false가 반환되면 컨트롤러 실행을 하지 않는다.
fasle라면 afterCompletion도 실행된다.


adapter의 handle로 요청을 처리한다. 반환값은 modelandview

선택된 requestmappinghandleradapter의 추상화클래스 AbstractHandlerMethodAdapter의 handle 메서드가 실행되고
requestmappinghandleradapter의 handleInternal메서드가 호출되고 그 내부에 invokeHandlerMethod가 실행됨.



기본적으로 27개의 argumentresolve와 15개의 returnValueHandler가 등록되어있다.


invokeAndHandle 메서드가 실제 컨트롤러에 요청을 보내고 응답값을
반환함. 비동기일경우 null로 클라이언트에 응답을 보내고 별도의 비동기 쓰레드에서 작업을 수행해 비동기적으로 응답. 동기일경우 바로 응답.
여기서 생성한 ModelAndViewContainer의 mavContainer변수에 메서드 실행 결과를 addAttribute한다.

invokeForRequest 메서드에서 컨트롤러 매개변수 판별 및 실행 후 응답값을 받는다.
여기서는 처리할 argumentResolve를 찾는다.



등록되있는 resolve들을 순회돌면서 supportsParameter가 true인 resolve를 찾아서 반환한다.
자체적으로 Cache용 변수를 사용하는데 한번 사용을하면 key로 저장된 값을 가져오도록하고 최초라면 가져온 후 다음에 key로 바로 값을 가져올 수 있도록 put을 한다.

현재 요청 파라미터를 RequestBody로 선언하였기때문에 RequestResponseBodyMethodProcessor 클래스의 supportsParameter 메서드가 true가 나오기때문에 해당 클래스가 선택된다.


요청객체 클래스 UserRequest가 선택되었다.



resolve에 기본적으로 9개의 messageconverter가 등록되있다.

messageconverter를 순회하면서 can.read가 true인 converter를 찾는다.
MappingJackson2HttpMessageConverter가 선택되었다.





내부에 @jsonview 확인하는것도 확인해볼수 있다.



AbstractJackson2HttpMessageConverter 클래스에서
ObjectReader.readvalue하고 있다.
genericConverter.read 에서 실제변환작업을하고
후처리 작업 등록시 getAdvice().afterBodyRead 에서 후처리 작업을 수행한다.


resolveArgument의 응답을 반환한다.

args[i] = this.resolvers.resolveArgument 결과값으로
args에 requestparameter가 userrequest에 변수로 세팅되있는걸 확인할 수 있다.


doInvoke에서는 요청값에 따른 응답을 생성한다.

getBridgeMethod는 컨트롤러에서 실행되는 메서드를 반환해주고
method.invoke로 메서드를 실행한다.

컨트롤러로 로직이 실행된다.

returnValue에 응답값이 세팅되었다.

returnValueHandlers.handleReturnValues 메서드가 실행된다.


selectHandler에서 응답핸들러를 찾는다.

기본저긍로 등록되있는 returnValueHandlers를 순회하면서 supportsReturnType 메서드가 true인걸 찾아서 반환한다.
응답으로 반환된 ResponseEntity는 HttpEntity.class를 상속받고있어 HttpEntityMethodProcessor클래스가 반환된다.
만약 ResponseEntity가 아니라 @ResponseBody나 @RestController 사용시 RequestResponseBodyMethodProcessor가 선택된다.
ResponseEntity가 @ResponseBody보다 우선순위가 높다.



handleReturnValue 메서드가 실행되고
mavContainer.setRequestHandled(true) 로 변경하고
returnValue를 확인한다.

writeWithMessageConverters 메서드가 실행되고
getBody로 body에 있는 응답객체를 넘긴다.


여기서 body의 클래스를 가져와서 valueType 클래스를 반환한다.


여기서 mediaType을 세팅하고

여기서 기본적으로 세팅되있는 9개의 messageConverter중에 mediaType을 지원하는것을 찾는다.

canWrite가 true인 MappingJackson2HttpMessageConverter가 선택된다.

genericConverter.write 메서드가 실행된다.

writeInternal 메서드가 실행된다.

addDefaultHeaders메서드에서 content-Type : json이 세팅된다.

writeInternal 메서드에서 selectObjectMapper에서 objectmapper가 세팅된다.

getJavaType 메서드에서 classType이 세팅된다.

writeValue 메서드 실행.

serialize 메서드 실행.

serializeValue 메서드 실행.

serialize 메서드 실행.

serialize 메서드 실행.

serializeFields 메서드 실행
props에서 직렬화할 변수 name과 age를 가져온다.

반복문에서 serializeAsFiled 호출

여기서 getter를 호출하고 있다.

body 정보를 flush한다.

정보를 모두 flush한다.


HttpEntityMethodProcessor 클래스의 handleReturnValue 메서드에서
isRequestHandled를 true로 선언하였기때문에 return null이 실행된다.

만약 viewName이 있다면 applyDefaultViewName 메서드에서 viewName 선언을 한다.

컨트롤러 요청이 다 끝나고 applyPostHandle 메서드에서 postHandle이 실행된다.

processDispatchResult 메서드를 실행한다.

오류가 발생하면 processHandlerException 가 실행되고

handlerExceptionResolvers를 순회하면서 존재한다면
resolveException 를 실행하고 errorView나 exceptionhandler로 전달해준다.

오류가 발생한다면 triggerAfterCompletion메서드에서 intereceptor에 afterCompletion 가 실행된다.

profile
웹 개발자

0개의 댓글