SpringMvc渲染視圖


這篇博文討論的問題是從ModelAndView如何渲染到頁面。

首先要知道每個請求處理完之后都會返回一個ModelAndView對象。

這里我分6種情況來分析,代表6種返回類型:

  1. ModelAndView
  2. Map,ModelMap
  3. Model
  4. View
  5. String
  6. Void

我先貼出我的測試的后台代碼:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!-- 配置掃描的包 -->
    <context:component-scan base-package="com"></context:component-scan>
    
    
    <!-- 配置視圖解析器,將方法的返回值映射到一個實際的物理視圖 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/pages/result/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven></mvc:annotation-driven>
 
  
</beans>
package com.mmc.modelandview;

import java.util.Date;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.InternalResourceView;
import org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView;

import com.mmc.common.CommonParam;


@Controller
public class TestModelAndView {
    
    @RequestMapping("testModelAndView")
    public ModelAndView testModelAndView(){
        ModelAndView modelAndView=new ModelAndView(CommonParam.SUCCESS);
        modelAndView.addObject("time", new Date());
        return modelAndView;
    }
    
    
    
    @RequestMapping("testModel")
    public String testModel(Model model){
        model.addAttribute("time",new Date());
        return CommonParam.SUCCESS;
    }
    
    @RequestMapping("testMap")
    public String testMap(Map<String,Object> map){
        map.put("person", "lixiaolu");
        return  CommonParam.SUCCESS;
    }
    
    
    @RequestMapping("testView")
    public View testView(View view){
        view=new JasperReportsPdfView();
        return view;
    }
    
    
    @RequestMapping("testString")
    public String testString(){
        return CommonParam.SUCCESS;
    } 
    
    @RequestMapping("testForward")
    public String testForward(){
        return "forward:/hello";
    }
    
    @RequestMapping("testVoid")
    public void testVoid(){
        System.out.println("執行testVoid方法");
    }
      
}

 

第一種:返回值是ModelAndView類型:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        String requestUri = urlPathHelper.getRequestUri(request);
                        logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                try {
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                }
                finally {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                }
           //檢查view有沒有設置,如果沒有給他設置一個默認的
                applyDefaultViewName(request, mv);
         //傳給我們的攔截器,也就是說在攔截器里我們可以操作視圖的映射 mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) { dispatchException = ex; }
       //就是去處理映射了 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }
catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }

這個方法進去之后,經過一些判斷,會執行到一個render(mv, request, response);方法,這個方法就是渲染視圖的方法。這個方法進去之后

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {        // Determine locale for request and apply it to the response.        Locale locale = this.localeResolver.resolveLocale(request); 
response.setLocale(locale); View view;
if (mv.isReference()) { // We need to resolve the view name.
       //返回一個View對象
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException( "Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // No need to lookup: the ModelAndView object contains the actual View object. view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } // Delegate to the View object for rendering. if (logger.isDebugEnabled()) { logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'"); } try {
        //渲染視圖 view.render(mv.getModelInternal(), request, response); }
catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'", ex); } throw ex; } }

 

@Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
                " and static attributes " + this.staticAttributes);
        }
      //將我ModelAndView中modelAndView.addObject("time", new Date());的值放入mergeModel中
        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
      //判斷是否讀取本地緩存
        prepareResponse(request, response);
      //進一步處理,下面貼出它的代碼 renderMergedOutputModel(mergedModel, request, response); }
@Overriprotected void renderMergedOutputModel(
 Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine which request handle to expose to the RequestDispatcher.
     //確定哪些請求處理暴露給RequestDispatcher。
HttpServletRequest requestToExpose = getRequestToExpose(request); // Expose the model object as request attributes.
     //把model里面的值放入request中
exposeModelAsRequestAttributes(model, requestToExpose); // Expose helpers as request attributes, if any. exposeHelpers(requestToExpose); // Determine the path for the request dispatcher.
     //獲取要返回的地址
String dispatcherPath = prepareForRendering(requestToExpose, response); // Obtain a RequestDispatcher for the target resource (typically a JSP).
     //確定一個請求處理器為這些參數資源
RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(requestToExpose, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.include(requestToExpose, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(requestToExpose, response); } }

當要轉發的地址,和要攜帶的信息都放入到requestToExpose里之后,就調用rd.forward(requestToExpose, response);轉發請求到頁面。

第二種:返回類型是Map

InvocableHandlerMethod類中有下面的方法:

public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
     //mavContainer對象初始化時,會初始化一個默認的BindingAwareModelMap屬性。將默認的這個BindingAwareModelMap屬性放入arg

Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { StringBuilder sb = new StringBuilder("Invoking ["); sb.append(this.getBeanType().getSimpleName()).append("."); sb.append(getMethod().getName()).append("] method with arguments "); sb.append(Arrays.asList(args)); logger.trace(sb.toString()); }
     //執行我的業務方法,把arg數組傳入,把我里面的mavContainer默認的modelMap賦上值 Object returnValue = invoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]"); } return returnValue; }

 然后將model取出來,和我返回的String一起構建ModelAndView對象。

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

        modelFactory.updateModel(webRequest, mavContainer);

        if (mavContainer.isRequestHandled()) {
            return null;
        }
     //取出model ModelMap model
= mavContainer.getModel();
   //構建對象 ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; }

 第三種:返回類型是Model

需要分析的就是我們加入model中的參數是怎么加入到modelAndView對象的。

其實是和Map類型也是一樣的,將

mavContainer對象初始化時,會初始化一個默認的BindingAwareModelMap屬性。將默認的這個BindingAwareModelMap屬性放入arg,然后把arg傳入我們的業務方法,然后業務方法接收到這個參數,往這個參數放值。就像我的例子里那樣
 public String testModel(Model model){
        model.addAttribute("time",new Date());
        return CommonParam.SUCCESS;
    }

放好之后,model就有值了,然后根據這個model和我返回的字符串一起構建ModelAndView對象。

第四種:返回類型是View

這種我還沒搞懂怎么用

第五種:返回類型是String

這個類ServletInvocableHandlerMethod的方法里:

public final void invokeAndHandle(ServletWebRequest webRequest,
            ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
      //這里返回我的String類型的值
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(this.responseReason)) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);

        try {
        //進一步處理返回值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } }

然后到ViewNameMethodReturnValueHandler類的這個方法里:

@Override
    public void handleReturnValue(
            Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws Exception {

        if (returnValue == null) {
            return;
        }
        else if (returnValue instanceof String) {
       //把返回值賦給mavContainer的ViewName String viewName
= (String) returnValue; mavContainer.setViewName(viewName); if (isRedirectViewName(viewName)) { mavContainer.setRedirectModelScenario(true); } } else { // should not happen throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } }

然后通過mavContainer對象來構造一個ModelAndView對象,然后渲染視圖。

第五種特別篇:返回值中帶有redirect的字符串

在ViewNameMethodReturnValueHandler類中的handleReturnValue方法中:

@Override
    public void handleReturnValue(
            Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws Exception {

        if (returnValue == null) {
            return;
        }
        else if (returnValue instanceof String) {
       //同樣是把帶有redirect的字符串賦給mavContainer對象。 String viewName
= (String) returnValue; mavContainer.setViewName(viewName); if (isRedirectViewName(viewName)) { mavContainer.setRedirectModelScenario(true); } } else { // should not happen throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } }

然后在DispatcherServlet類中有這個方法:

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {

        for (ViewResolver viewResolver : this.viewResolvers) {
       //獲取view,View是一個接口,這里的view實際類型是RedirectView View view
= viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; }

然后根據View類型的不同,調用的也是不同的renderMergedOutputModel方法,這是RedirectView類的方法:

@Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        String targetUrl = createTargetUrl(model, request);
        targetUrl = updateTargetUrl(targetUrl, model, request, response);

        FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
        if (!CollectionUtils.isEmpty(flashMap)) {
            UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build();
            flashMap.setTargetRequestPath(uriComponents.getPath());
            flashMap.addTargetRequestParams(uriComponents.getQueryParams());
            FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
            if (flashMapManager == null) {
                throw new IllegalStateException("FlashMapManager not found despite output FlashMap having been set");
            }
            flashMapManager.saveOutputFlashMap(flashMap, request, response);
        }

        sendRedirect(request, response, targetUrl, this.http10Compatible);
    }

然后就是response.sendRedirect(encodedRedirectURL);

第五種特別篇之帶forward的字符串:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Determine locale for request and apply it to the response.
        Locale locale = this.localeResolver.resolveLocale(request);
        response.setLocale(locale);

        View view;
        if (mv.isReference()) {
            // We need to resolve the view name.
       //選擇不同的類型的視圖處理器構建視圖對象
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); if (view == null) { throw new ServletException( "Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } } else { // No need to lookup: the ModelAndView object contains the actual View object. view = mv.getView(); if (view == null) { throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " + "View object in servlet with name '" + getServletName() + "'"); } } // Delegate to the View object for rendering. if (logger.isDebugEnabled()) { logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'"); } try { view.render(mv.getModelInternal(), request, response); } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'", ex); } throw ex; } }

這個是DispatcherServlet中的resolveViewName方法。

然后是選擇了AbstractCachingViewResolver這個視圖處理器的resolveViewName方法,去創建視圖。然后調用了他的子類UrlBasedViewResolver的創建視圖方法:

@Override
    protected View createView(String viewName, Locale locale) throws Exception {
        // If this resolver is not supposed to handle the given view,
        // return null to pass on to the next resolver in the chain.
        if (!canHandle(viewName, locale)) {
            return null;
        }
        // Check for special "redirect:" prefix.
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
            return applyLifecycleMethods(viewName, view);
        }
        // Check for special "forward:" prefix.
     //獲得一個forwardUrl
if (viewName.startsWith(FORWARD_URL_PREFIX)) { String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length()); return new InternalResourceView(forwardUrl); } // Else fall back to superclass implementation: calling loadView. return super.createView(viewName, locale); }

然后調用InternalResourceView類的renderMergedOutputModel方法:

@Override
    protected void renderMergedOutputModel(
            Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // Determine which request handle to expose to the RequestDispatcher.
        HttpServletRequest requestToExpose = getRequestToExpose(request);

        // Expose the model object as request attributes.
        exposeModelAsRequestAttributes(model, requestToExpose);

        // Expose helpers as request attributes, if any.
        exposeHelpers(requestToExpose);

        // Determine the path for the request dispatcher.
        String dispatcherPath = prepareForRendering(requestToExpose, response);

        // Obtain a RequestDispatcher for the target resource (typically a JSP).
        RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
        if (rd == null) {
            throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                    "]: Check that the corresponding file exists within your web application archive!");
        }

        // If already included or response already committed, perform include, else forward.
        if (useInclude(requestToExpose, response)) {
            response.setContentType(getContentType());
            if (logger.isDebugEnabled()) {
                logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
            }
            rd.include(requestToExpose, response);
        }

        else {
            // Note: The forwarded resource is supposed to determine the content type itself.
            if (logger.isDebugEnabled()) {
                logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
            }
       //調用轉發方法 rd.forward(requestToExpose, response); } }

 第六種:返回類型是Void

這時我們需要考慮兩個問題,第一:返回值是Void也就是沒有返回值,他的ModalAndView對象是什么樣的?第二:他將轉到哪個頁面,還是會直接報錯?

帶着這兩個問題,我們看源碼:

以DispatcherServlet中的這句代碼往下看,

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

一直到ServletInvocableHandlerMethod類的這個方法

public final void invokeAndHandle(ServletWebRequest webRequest,
            ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        }
        else if (StringUtils.hasText(this.responseReason)) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);

        try {
       //主要看這里,handleReturnValue,處理返回值,那我們點進去看看他怎么處理null值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } }

然而結果就是他看到return是null,什么都沒處理就返回了,只好再往下看。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        String requestUri = urlPathHelper.getRequestUri(request);
                        logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                try {
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                }
                finally {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                }
          //最后發現view為null時,這里處理了
                applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                return;
            }
            // Clean up any resources used by a multipart request.
            if (multipartRequestParsed) {
                cleanupMultipart(processedRequest);
            }
        }
    }

接下來是組裝地址的方法:這個是處理地址組裝的類UrlBasedViewResolver

@Override
    protected View loadView(String viewName, Locale locale) throws Exception {
     //進行組裝 AbstractUrlBasedView view
= buildView(viewName); View result = applyLifecycleMethods(viewName, view); return (view.checkResource(locale) ? result : null); }
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
     //對應的我最上方spring.mvc里面的prefix和suffix的配置的地址,把它們連接起來 view.setUrl(getPrefix()
+ viewName + getSuffix()); String contentType = getContentType(); if (contentType != null) { view.setContentType(contentType); } view.setRequestContextAttribute(getRequestContextAttribute()); view.setAttributesMap(getAttributesMap()); if (this.exposePathVariables != null) { view.setExposePathVariables(exposePathVariables); } return view; }

到此,要轉向哪一個地址,ModelAndView對象是什么樣都已經大致清楚了。然后就是調用rd.forward(requestToExpose, response);方法,渲染到頁面

 

里面代碼很多,思路也不是很清楚。有意義的地方只是給小伙伴們提供了幾個方向,要想看springMvc是如何渲染視圖的,可以從他的返回類型入手,然后debug一步步的看。源碼我是在這里:

https://mvnrepository.com/search?q=spring-ui&p=2     下載的。我這里用的是4.0.0的版本。最后在總結一下吧。

首先,要構造一個ModelAndView對象,這個對象的構建思路是,我創建一個數組,把一個對象放在這個數組里,然后把數組給你,你往這個數組里放值,然后我去取這個數組里的值。然后獲取的值我拿來

創建一個ModelAndView對象。

第二步,去渲染視圖,有很多的渲染視圖的處理器,系統會去判斷選擇一個處理器,來處理你的請求,大概的不同就是,我最終是調doFord還是doRedirect,我的返回值的header應該設置什么等等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM