컨트롤러에 요청을 하면
DispatcherServlet 클래스에서 핸들러를 찾고 handle 함수 실행
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
반환 타입은 ModelAndView 로 반환
RequestMappingHandlerAdapter 에서
handleInternal 메소드 실행
mav = this.invokeHandlerMethod(request, response, handlerMethod);
그리고 위 메소드에서
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer 생성한다
다음 로직에서 계속해서 mavContainer를 인자로 넣어준다
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
ServletInvocableHandlerMethod 클래스에서
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 여기서 요청을 실행
Object returnValue = this.invokeForReque
위 메소드 실행
이후에 returnValueHandler를 통해서 뷰이름 저장하는 로직을 탐
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
ViewNameMethodReturnValueHandler 로 가면 됨
return 할때 view name 을 하면 타입이 String으로 나올 것이다
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return Void.TYPE == paramType || CharSequence.class.isAssignableFrom(paramType);
}
문자열이면 true 를 반환하고 handleReturnValue 이 메소드 실행
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
// 컨테이너에 viewName 저장
mavContainer.setViewName(viewName);
if (this.isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
} else if (returnValue != null) {
String var10002 = returnType.getParameterType().getName();
throw new UnsupportedOperationException("Unexpected return type: " + var10002 + " in method: " + returnType.getMethod());
}
}
이렇게 하고서
다시 RequestMappingHandlerAdapter로 가고 invokeHandlerMethod 에서 ModelAndView 를 가져온다
return asyncManager.isConcurrentHandlingStarted() ? null : this.getModelAndView(mavContainer, modelFactory, webRequest);
}
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 결과가 false 이므로 아래의 modelAndView 로직이 실행
if (mavContainer.isRequestHandled()) {
return null;
} else {
모델을 가져오고 뷰이름과 모델 그리고 상태코드를 넣어서 객체를 만든다
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
로직을 타서 modelAndView 객체가 완성이 된다
DispatcherServlet에서 이 객체를 리턴받고
this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
해당 메소드에서 viewResolver를 선택하고 rendering 한다
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
뷰이름을 꺼내고 응답을 처리할 뷰를 가져온다
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
response.setLocale(locale);
String viewName = mv.getViewName();
View view;
if (viewName != null) {
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
view 를 꺼내기 위해 viewResolver를 가져온다
viewResolver는 하나가 아니라 여러개이다
@Nullable
private View resolveViewNameInternal(String viewName, Locale locale) throws Exception {
if (this.viewResolvers != null) {
for(ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
ContentNegotiatingViewResolver에서 ThymeleafView를 리턴한다
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if (requestedMediaTypes != null) {
List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
그러고서
ThymeleafView 클래스
렌더링함
public void render(final Map<String, ?> model, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
this.renderFragment(this.markupSelectors, model, request, response);
}