这篇博文讨论的问题是从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应该设置什么等等。

SpringMvc渲染视图的更多相关文章

  1. 学习SpringMVC——说说视图解析器

    各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~) 相信大家在昨天那篇如何获取请求参数篇中都已经领略到了spring mvc注 ...

  2. SpringMVC重定向视图RedirectView小分析

    目录 前言 RedirectView介绍 实例讲解 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnbl ...

  3. SpringMvc:视图和视图解析器

    请求处理方法执行完成后,最终返回一个ModelAndView对象,对于返回String,View或ModelMap等类型的处理方法,SpringMvc也会在内部将它们装配成一个ModelAndView ...

  4. SpringMVC——说说视图解析器

    学习SpringMVC——说说视图解析器   各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~) 相信大家在昨天那篇如何获取请 ...

  5. 学习笔记(二)——MVC扩展(渲染视图)

    如何渲染视图? 我以近乎的视图引擎为例总结了一下,近乎中的ThemedViewEngine类,就是重写后的的视图引擎.ThemedViewEngine类主要对FindPartialView和FindV ...

  6. ASP.NET Core中使用Razor视图引擎渲染视图为字符串

    一.前言 在有些项目需求上或许需要根据模板生产静态页面,那么你一样可以用Razor语法去直接解析你的页面从而把解析的页面生成静态页,这样的使用场景很多,不限于生成静态页面,视图引擎为我们提供了模型到视 ...

  7. ASP.NET Core中使用Razor视图引擎渲染视图为字符串(转)

    一.视图渲染说明 在有些项目需求上或许需要根据模板生产静态页面,那么你一样可以用Razor语法去直接解析你的页面从而把解析的页面生成静态页,这样的使用场景很多,不限于生成静态页面,视图引擎为我们提供了 ...

  8. SpringMVC自定义视图Excel视图和PDF视图

    SpringMVC自定义视图 Excel视图和PDF视图 SpringMVC杂记(十一) 使用Excel视图 Spring MVC 视图解析器(ViewResolver ) java实现导出excel ...

  9. SpringMVC 多视图解析器配置以及问题

    在SpringMVC模式当中可以通过如下配置来支持多视图解析 <!-- jsp jstl --> <bean id="JSPViewResolver" class ...

随机推荐

  1. transform.rotation和GetComponent<Rigidbody>().MoveRotation

    同时在UPDATE和FIXED UPDATE中调整 旋转 并未出现闪,而是一直以UPDATE中的为准,可认为MoveRotation调用后在UPDATE中生效 using System.Collect ...

  2. 接雨水12 · Trapping Rain Water12

    [抄题]: Given n non-negative integers representing an elevation map where the width of each bar is 1, ...

  3. 图是否是树 · Graph Valid Tree

    [抄题]: 给出 n 个节点,标号分别从 0 到 n - 1 并且给出一个 无向边的列表 (给出每条边的两个顶点), 写一个函数去判断这张`无向`图是否是一棵树. 给出n = 5 并且 edges = ...

  4. 部署MVC项目ManagedPipelineHandler报错

    "处理程序ExtensionlessUrlHandler-Integrated-4.0在其模块列表中有一个错误模块ManagedPipelineHandler": 解决方法:以管理 ...

  5. Financial Information Exchange (FIX) Protocol Interview Questions Answers[z]

    What do you mean by Warrant?Warrant is a financial product which gives right to holder to Buy or Sel ...

  6. Vue 路由缓存

    问题 在路由切换时不需要每次 点击都刷新子路由   尤其是在form表单的情况下  不能让用户 输入一半之后点击其他页面  再点回来 表单数据不见了 解决方案   vue 2.0     之中  有k ...

  7. 记录GOPATH在GOLAND中的坑

    首先我的环境已配置好: GO的目录结构是: add.go package calc//函数名必须大写首字母,不然外部包找不到 func Add(a int,b int)(int){ return a+ ...

  8. [Linux] Big-endian and Little-endian (大小端模式)

    Big-endian Little-endian 大小端模式   https://en.wikipedia.org/wiki/Endianness 大端模式,是指数据的高字节保存在内存的低地址中,而数 ...

  9. openpose

    编译libpthread.so pthread2 ihash

  10. Java 设计模式系列(九)组合模式

    Java 设计模式系列(九)组合模式 将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象的使用具有一致性. 一.组合模式结构 Component: 抽象 ...