SpringMvc渲染视图
这篇博文讨论的问题是从ModelAndView如何渲染到页面。
首先要知道每个请求处理完之后都会返回一个ModelAndView对象。
这里我分6种情况来分析,代表6种返回类型:
我先贴出我的测试的后台代码:
<?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渲染视图的更多相关文章
- 学习SpringMVC——说说视图解析器
各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~) 相信大家在昨天那篇如何获取请求参数篇中都已经领略到了spring mvc注 ...
- SpringMVC重定向视图RedirectView小分析
目录 前言 RedirectView介绍 实例讲解 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnbl ...
- SpringMvc:视图和视图解析器
请求处理方法执行完成后,最终返回一个ModelAndView对象,对于返回String,View或ModelMap等类型的处理方法,SpringMvc也会在内部将它们装配成一个ModelAndView ...
- SpringMVC——说说视图解析器
学习SpringMVC——说说视图解析器 各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~) 相信大家在昨天那篇如何获取请 ...
- 学习笔记(二)——MVC扩展(渲染视图)
如何渲染视图? 我以近乎的视图引擎为例总结了一下,近乎中的ThemedViewEngine类,就是重写后的的视图引擎.ThemedViewEngine类主要对FindPartialView和FindV ...
- ASP.NET Core中使用Razor视图引擎渲染视图为字符串
一.前言 在有些项目需求上或许需要根据模板生产静态页面,那么你一样可以用Razor语法去直接解析你的页面从而把解析的页面生成静态页,这样的使用场景很多,不限于生成静态页面,视图引擎为我们提供了模型到视 ...
- ASP.NET Core中使用Razor视图引擎渲染视图为字符串(转)
一.视图渲染说明 在有些项目需求上或许需要根据模板生产静态页面,那么你一样可以用Razor语法去直接解析你的页面从而把解析的页面生成静态页,这样的使用场景很多,不限于生成静态页面,视图引擎为我们提供了 ...
- SpringMVC自定义视图Excel视图和PDF视图
SpringMVC自定义视图 Excel视图和PDF视图 SpringMVC杂记(十一) 使用Excel视图 Spring MVC 视图解析器(ViewResolver ) java实现导出excel ...
- SpringMVC 多视图解析器配置以及问题
在SpringMVC模式当中可以通过如下配置来支持多视图解析 <!-- jsp jstl --> <bean id="JSPViewResolver" class ...
随机推荐
- grep 过滤.svn文件
[grep 过滤.svn文件] 问题: 在repository搜索代码时,常常会搜索到.svn的代码,如果不想搜索.svn目录下的相关代码怎么办? 1.使用管道进行双层“过滤”,其中第二次gre ...
- springboot 配置jsp支持
springboot默认并不支持jsp模板,所以需要配置. 下面是一个可以运行的例子: 首先配置属性文件: spring.http.encoding.force=true spring.http. ...
- 未能加载文件或程序集"xxxxxx"或它的某一个依赖项
错误:未能加载文件或程序集“xxx”或它的某一个依赖项.试图加载格式不正确的程序. 原因分析:操作系统是64位的,但发布的程序引用了一些32位的ddl,所以出现了兼容性的问题. 解决方案:IIS——应 ...
- ubuntu下安装最新的nodejs
# apt-get update # apt-get install -y python-software-properties software-properties-common # add-ap ...
- Halcon中的坐标系特点及XLD的镜像转换
我们知道,Halcon中的坐标系的原点在左上角,而一般二维平面坐标系的原点在左下角.那么Halcon中坐标系和一般的二维坐标系有什么区别呢?我通过下面这个例子来分析. gen_image_const ...
- SQL获取当前日期的年、月、日、时、分、秒数据
SQL Server中获取当前日期的年.月.日.时.分.秒数据: SELECT GETDATE() as '当前日期',DateName(year,GetDate()) as '年',DateName ...
- js 右击事件
$.fn.extend({ "rightclick": function (fn) { $(this).mousedown(function ( ...
- 无脑无负担网站架构-- Application Request Route的一些应用
首先作为一个.net 程序员,多数情况你懒的整什么架构啊.框架啊.还有那命令行的linux,别说linux也有桌面,那个桌面用起来更让人抓狂,一直不明白居然有人说喜欢上linux的命令行,装B还是SB ...
- 利用BeanUtils.copyProperties 克隆出新对象,避免对象重复问题
1.经常用jQuery获取标签里面值val(),或者html(),text()等等,有次想把获取标签的全部html元素包括自己也用来操作,查询了半天发现$("#lefttr1"). ...
- Nginx搭建后,图片存储在Tomcat上,前端无法回显图片问题
一.Nginx与Tomcat连接搭建的环境,Nginx设置了前端的访问路径为 (1)前端代码配置: root /usr/local/nginx/html; index index.html index ...