View/ViewResolver

tokkaiiii·2025년 5월 14일

spring-mvc

목록 보기
21/27

컨트롤러에 요청을 하면
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);
  }
profile
풀스택 자바 개발자입니다

0개의 댓글