javascript
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.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://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("執(zhí)行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有沒有設(shè)置,如果沒有給他設(shè)置一個默認的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);}}}
這個方法進去之后,經(jīng)過一些判斷,會執(zhí)行到一個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;}}
?
@Overridepublic 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).
//確定一個請求處理器為這些參數(shù)資源
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);}}
當要轉(zhuǎn)發(fā)的地址,和要攜帶的信息都放入到requestToExpose里之后,就調(diào)用rd.forward(requestToExpose, response);轉(zhuǎn)發(fā)請求到頁面。
第二種:返回類型是Map
InvocableHandlerMethod類中有下面的方法:
public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//mavContainer對象初始化時,會初始化一個默認的BindingAwareModelMap屬性。將默認的這個BindingAwareModelMap屬性放入argObject[] 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()); } //執(zhí)行我的業(yè)務(wù)方法,把arg數(shù)組傳入,把我里面的mavContainer默認的modelMap賦上值Object returnValue = invoke(args);if (logger.isTraceEnabled()) {logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]");}return returnValue;}
?然后將model取出來,和我返回的String一起構(gòu)建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();
//構(gòu)建對象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中的參數(shù)是怎么加入到modelAndView對象的。
其實是和Map類型也是一樣的,將
mavContainer對象初始化時,會初始化一個默認的BindingAwareModelMap屬性。將默認的這個BindingAwareModelMap屬性放入arg,然后把arg傳入我們的業(yè)務(wù)方法,然后業(yè)務(wù)方法接收到這個參數(shù),往這個參數(shù)放值。就像我的例子里那樣public String testModel(Model model){model.addAttribute("time",new Date());return CommonParam.SUCCESS;}
放好之后,model就有值了,然后根據(jù)這個model和我返回的字符串一起構(gòu)建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類的這個方法里:
@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws Exception {if (returnValue == null) {return;}else if (returnValue instanceof String) {//把返回值賦給mavContainer的ViewNameString viewName = (String) returnValue;mavContainer.setViewName(viewName);if (isRedirectViewName(viewName)) {mavContainer.setRedirectModelScenario(true);}}else {// should not happenthrow new UnsupportedOperationException("Unexpected return type: " +returnType.getParameterType().getName() + " in method: " + returnType.getMethod());}}
然后通過mavContainer對象來構(gòu)造一個ModelAndView對象,然后渲染視圖。
第五種特別篇:返回值中帶有redirect的字符串
在ViewNameMethodReturnValueHandler類中的handleReturnValue方法中:
@Overridepublic 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 happenthrow 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;}
然后根據(jù)View類型的不同,調(diào)用的也是不同的renderMergedOutputModel方法,這是RedirectView類的方法:
@Overrideprotected 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.//選擇不同的類型的視圖處理器構(gòu)建視圖對象
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方法,去創(chuàng)建視圖。然后調(diào)用了他的子類UrlBasedViewResolver的創(chuàng)建視圖方法:
@Overrideprotected 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);}
然后調(diào)用InternalResourceView類的renderMergedOutputModel方法:
@Overrideprotected 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() + "'");}//調(diào)用轉(zhuǎn)發(fā)方法 rd.forward(requestToExpose, response);}}
?第六種:返回類型是Void
這時我們需要考慮兩個問題,第一:返回值是Void也就是沒有返回值,他的ModalAndView對象是什么樣的?第二:他將轉(zhuǎn)到哪個頁面,還是會直接報錯?
帶著這兩個問題,我們看源碼:
以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;}}
然而結(jié)果就是他看到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;}}//最后發(fā)現(xiàn)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
@Overrideprotected 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());
//對應(yīng)的我最上方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;}
到此,要轉(zhuǎn)向哪一個地址,ModelAndView對象是什么樣都已經(jīng)大致清楚了。然后就是調(diào)用rd.forward(requestToExpose, response);方法,渲染到頁面
?
里面代碼很多,思路也不是很清楚。有意義的地方只是給小伙伴們提供了幾個方向,要想看springMvc是如何渲染視圖的,可以從他的返回類型入手,然后debug一步步的看。源碼我是在這里:
https://mvnrepository.com/search?q=spring-ui&p=2? ? ?下載的。我這里用的是4.0.0的版本。最后在總結(jié)一下吧。
首先,要構(gòu)造一個ModelAndView對象,這個對象的構(gòu)建思路是,我創(chuàng)建一個數(shù)組,把一個對象放在這個數(shù)組里,然后把數(shù)組給你,你往這個數(shù)組里放值,然后我去取這個數(shù)組里的值。然后獲取的值我拿來
創(chuàng)建一個ModelAndView對象。
第二步,去渲染視圖,有很多的渲染視圖的處理器,系統(tǒng)會去判斷選擇一個處理器,來處理你的請求,大概的不同就是,我最終是調(diào)doFord還是doRedirect,我的返回值的header應(yīng)該設(shè)置什么等等。
轉(zhuǎn)載于:https://www.cnblogs.com/javammc/p/8570823.html
總結(jié)
以上是生活随笔為你收集整理的SpringMvc渲染视图的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jdbc之连接Oracle的基本步骤
- 下一篇: A.PHP读取txt文本文件并分页显示的