[Spring] Dispatcher Servlet 파헤치기 (Argument Resolver, ReturnValue Handler, Controller 실행시점)
Argument Resolver, ReturnValue Handler는 Dispatcher Servlet이 Handler Mapping 과정에서 등록, 사용되는 것을 이전 포스팅에서 확인할 수 있었다.
[Spring] Argument Resolver 개념 / Custom Argument Resolver 생성
[Spring] Return Value Handler 개념 / Custom Return Value Handler 생성
이번 포스팅에서는 소스레벨레벨에서 데이터가 어떻게 호출되고 관리되는지 확인해 보도록 하겠다.
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = null;
...
// (2) Handler 조회
// URL에 맵핑된 Handler(Controller)를 찾는다.
mappedHandler = getHandler(processedRequest);
// (3) Handler Adapter 조회
// Handler(Controller)를 수행할 수 있는 HandlerAdapter를 찾는다.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
// (4) Handler Adapter 호출
// (5) Handler 호출
// (6) ModelAndView 반환
// HandlerAdapter를 통해서 Handler를 실행한다.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// (7) ViewResolver 호출
// (8) View반환
// (9) 응답
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
doDispatch 메서드는 dispatch 영문 해석 그대로 Server와 Client에게 정보를 전달하는 역할을 한다.
동작구조의 순서를 메서드에 매핑한 내용이다.
// (4) Handler Adapter 호출
// (5) Handler 호출
// (6) ModelAndView 반환
// HandlerAdapter를 통해서 Handler를 실행한다.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
(4) Handler Adapter 호출, (5) Handler 호출, (6) ModelAndView 반환의 내용을 소스로 분석해 보자.
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected abstract ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
}
AbstractHandlerMethodAdapter의 handle메서드 호출.
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
...
mav = invokeHandlerMethod(request, response, handlerMethod);
...
return mav;
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
…
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
…
// Handler 호출
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
}
RequestMappingHandlerAdapter는 AbstractHandlerMethodAdapter를 상속받고 있어 결국 handleInternal 메서드가 호출된다.
handleInternal에서 invokeHandlerMethod를 호출한다.
ModelAndViewContainer가 중요한 객체이다.
// Handler 호출
invocableMethod.invokeAndHandle(webRequest, mavContainer);
Handler(Controller) 실행결과가 mavContainer에 담겨있다.
return getModelAndView(mavContainer, modelFactory, webRequest);
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//생략
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
//생략
return mav;
}
실행결과는 mavContainer에 담겨있다고 했다. mavContainer에서 결과(Model)를 꺼내 ModelAndView 생성하여 DispatcherServlet로 반환한다.
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
try {
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
}
위에서 RequestMappingHandlerAdapter의 invokeHandlerMethod메서드에서 invokeAndHandle메서드가 호출되는 것을 확인하였다.
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
Handler(Controller)에 적합한 ArgumentResolver가동작 한 후 Handler(Controller)가 실행된다.
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);
}
genMethodArgumentValues메서드를 들어가 보면 ArgumentResolver의 중요 메서드 supportsParameter, resolveArgument가 호출되는 것을 확인할 수 있다.
doInvoke(args);
우리가 그림으로 확인했던 Handler(Controller)가 호출되는 부분이 여기다.
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
Handle(Controller) 실행결과를 처리하는 ReturnValueHandler가 실행된다.
ArgumentResolver, ReturnValueHandler가 이 부분에서 호출된다.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
DispatcherServlet의 HandlerAdaper의 handle메서드호출이 끝나고 ModelAndView에 실행결과가 담겨있게 되었다.
(7) ViwResolver 호출, (8) View반환, (9) 응답 부분을 확인해 보자.
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// (7) ViewResolver 호출
// (8) View반환
// (9) 응답
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
...
// 뷰 렌더링 호출
render(mv, request, response);
...
}
protected void render(ModelAndView mv, HttpServletRequest request,HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if(viewName != null){
// (7) ViewResolver 호출
// (8) View반환
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
else {
// ModelAndView 객체에 View 객체가 포함되어 있다.
view = mv.getView();
}
// (9) 응답 (뷰 렌더링)
view.render(mv.getModelInternal(), request, response);
}
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
Handler(Controller) 실행 결과 ModelAndView를 적절한 response 형태로 처리한다.
protected void render(ModelAndView mv, HttpServletRequest request,HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if(viewName != null){
// (7) ViewResolver 호출
// (8) View반환
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
else {
// ModelAndView 객체에 View 객체가 포함되어 있다.
view = mv.getView();
}
// (9) 응답 (뷰 렌더링)
view.render(mv.getModelInternal(), request, response);
}
등록된 ViewResolver 중 뷰 이름을 통해 처리할 수 있는 뷰 리졸버를 찾거나 ModelAndView에 이미 View가 포함되어 있으면 꺼내서 사용한다.
view.render(mv.getModelInternal(), request, response);
View 구현체마다 렌더링을 하는 방식이 다르다.
SLF4J 동작과정 개념정리 (0) | 2024.11.21 |
---|---|
log4jdbc 개념정리 (1) | 2024.11.13 |
[Spring] Return Value Handler 개념 / Custom Return Value Handler 생성 (1) | 2024.06.17 |
[Spring] Argument Resolver 개념 / Custom Argument Resolver 생성 (33) | 2024.06.17 |
@Transactional 기초 및 주의사항 (8) | 2023.05.06 |
댓글 영역