首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

SpringMVC 核心源碼深度解析:視圖解析與渲染全流程

     編輯:Java 綜合教學

核心入口:DispatcherServlet.doDispatch

// 1. 處理器執行,返回 ModelAndView
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 2. 結果處理:視圖解析 + 渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

processDispatchResult → render 流程

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) {
    Locale locale = localeResolver.resolveLocale(request);
    response.setLocale(locale);
    
    View view;
    if (mv.isReference()) {
        // === 視圖解析階段 ===
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        if (view == null) throw new ServletException("Could not resolve view: " + mv.getViewName());
    } else {
        // 直接包含 View 對象
        view = mv.getView();
    }
    
    // === 視圖渲染階段 ===
    view.render(mv.getModelInternal(), request, response);
}

視圖解析:ViewResolver 鏈機制

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) {
    for (ViewResolver resolver : viewResolvers) {
        View view = resolver.resolveViewName(viewName, locale);
        if (view != null) return view; // 首個成功解析即返回
    }
    return null;
}

優先級控制:order 屬性

  • 默認 order = Integer.MAX_VALUE (2147483647)
  • ContentNegotiatingViewResolver 默認 Ordered.HIGHEST_PRECEDENCE (-2147483648)
  • order 值越小,越優先被遍歷
  • 同優先級按 XML 配置順序遍歷

常用 ViewResolver 類型

解析器用途關鍵屬性
InternalResourceViewResolverJSP/內部資源轉發prefix, suffix
BeanNameViewResolverBean 名稱即視圖名無
FreeMarkerViewResolverFreeMarker 模板prefix, suffix, contentType
ThymeleafViewResolverThymeleaf 模板templateMode, characterEncoding
ContentNegotiatingViewResolver根據 Accept Header/參數協商mediaTypes, defaultViews
JsonView / MappingJackson2JsonViewJSON 直接輸出無

InternalResourceViewResolver 解析邏輯

// UrlBasedViewResolver.createView
if (viewName.startsWith("redirect:")) {
    return new RedirectView(viewName.substring("redirect:".length()));
}
if (viewName.startsWith("forward:")) {
    return new InternalResourceView(viewName.substring("forward:".length()));
}
// 默認:prefix + viewName + suffix → JstlView/InternalResourceView
String url = getPrefix() + viewName + getSuffix();

BeanNameViewResolver 解析邏輯

public View resolveViewName(String viewName, Locale locale) {
    if (!context.containsBean(viewName)) return null; // 支持鏈式
    return context.getBean(viewName, View.class); // 從容器獲取 View Bean
}

視圖渲染:View 接口與實現

public interface View {
    String getContentType();
    void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response);
}

AbstractView.render 模板方法

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
    Map<String, Object> merged = createMergedOutputModel(model, request, response);
    prepareResponse(request, response);
    renderMergedOutputModel(merged, request, response); // 子類實現
}

InternalResourceView.renderMergedOutputModel — 轉發機制

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
    // 1. 模型屬性暴露為請求屬性
    exposeModelAsRequestAttributes(model, requestToExpose);
    exposeHelpers(requestToExpose);
    
    // 2. 獲取 RequestDispatcher
    String dispatcherPath = prepareForRendering(requestToExpose, response);
    RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
    
    // 3. 轉發或包含
    if (useInclude(requestToExpose, response)) {
        rd.include(requestToExpose, response); // include
    } else {
        rd.forward(requestToExpose, response); // forward(默認)
    }
}

完整流程總結圖

  1. Controller 返回 ModelAndView(視圖名 + 模型)
  2. DispatcherServlet 遍歷 ViewResolver 鏈,首個成功解析返回 View
  3. View.render(model, request, response) 執行渲染
  4. InternalResourceView 將模型放入請求屬性,RequestDispatcher.forward() 到 JSP
  5. JSP 執行,輸出 HTML 響應

最佳實踐建議

  • 配置 InternalResourceViewResolver 時設置 order 確保優先級
  • REST API 直接返回 @ResponseBody / ResponseEntity,繞過視圖解析
  • 異常視圖配置 SimpleMappingExceptionResolver 或 @ControllerAdvice
  • 國際化視圖:ResourceBundleViewResolver 或 ContentNegotiatingViewResolver
  • 避免在 Controller 中硬編碼視圖路徑,使用邏輯視圖名

視圖解析與渲染是 SpringMVC 將模型數據轉化為用戶可感知響應的關鍵環節,掌握其源碼流程有助於排查 404、視圖不生效、亂碼等疑難問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved