zoukankan      html  css  js  c++  java
  • 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应该设置什么等等。

  • 相关阅读:
    js-20170816-Date对象
    js-20170804-Math对象
    js-20170619-string对象
    微信web开发者工具
    前端开发指南
    接口调试工具DHC
    CSS预处理器
    Weex
    Vue.js
    hbuilder
  • 原文地址:https://www.cnblogs.com/javammc/p/8570823.html
Copyright © 2011-2022 走看看