Spring MVC提供了以下几种途径输出模型数据:
1)ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据;
2)Map及Model:处理方法入参为org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map时,处理方法返回时,Map中的数据会自动被添加到模型中;
3)@SessionAttributes:将模型中的某个属性暂存到HttpSeession中,以便多个请求之间可以共享这个属性;
4)@ModelAttribute:方法入参标注该注解后,入参的对象就会放到数据模型中。
ModelAndView
用法示例:
添加TestModelAndView.java handler类:
package com.dx.springlearn.hanlders; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class TestModelData { private final String SUCCESS = "success"; @RequestMapping("/testModelAndView") public ModelAndView testModelAndView() { String viewName = SUCCESS; ModelAndView modelAndView = new ModelAndView(viewName); modelAndView.addObject("currentTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); return modelAndView; } }
修改index.jsp,添加链接:
<a href="testModelAndView">test ModelAndView</a> <br />
修改/WEB-INF/views/success.jsp,编辑添加内容:
current time:${requestScope.currentTime} <br>
点击链接地址,显示结果:
对TestModelAndView.java中“ modelAndView.addObject("currentTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));”该行添加断点,进行调试:
根据调试信息,索引到DispatcherServlet的doDispatcher方法中:
DispatcherServlet的doDispatcher方法源代码为:
1 /** 2 * Process the actual dispatching to the handler. 3 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. 4 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters 5 * to find the first that supports the handler class. 6 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers 7 * themselves to decide which methods are acceptable. 8 * @param request current HTTP request 9 * @param response current HTTP response 10 * @throws Exception in case of any kind of processing failure 11 */ 12 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 13 HttpServletRequest processedRequest = request; 14 HandlerExecutionChain mappedHandler = null; 15 boolean multipartRequestParsed = false; 16 17 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 18 19 try { 20 ModelAndView mv = null; 21 Exception dispatchException = null; 22 23 try { 24 processedRequest = checkMultipart(request); 25 multipartRequestParsed = (processedRequest != request); 26 27 // Determine handler for the current request. 28 mappedHandler = getHandler(processedRequest); 29 if (mappedHandler == null) { 30 noHandlerFound(processedRequest, response); 31 return; 32 } 33 34 // Determine handler adapter for the current request. 35 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 36 37 // Process last-modified header, if supported by the handler. 38 String method = request.getMethod(); 39 boolean isGet = "GET".equals(method); 40 if (isGet || "HEAD".equals(method)) { 41 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 42 if (logger.isDebugEnabled()) { 43 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); 44 } 45 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 46 return; 47 } 48 } 49 50 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 51 return; 52 } 53 54 // Actually invoke the handler. 55 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 56 57 if (asyncManager.isConcurrentHandlingStarted()) { 58 return; 59 } 60 61 applyDefaultViewName(processedRequest, mv); 62 mappedHandler.applyPostHandle(processedRequest, response, mv); 63 } 64 catch (Exception ex) { 65 dispatchException = ex; 66 } 67 catch (Throwable err) { 68 // As of 4.3, we're processing Errors thrown from handler methods as well, 69 // making them available for @ExceptionHandler methods and other scenarios. 70 dispatchException = new NestedServletException("Handler dispatch failed", err); 71 } 72 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 73 } 74 catch (Exception ex) { 75 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 76 } 77 catch (Throwable err) { 78 triggerAfterCompletion(processedRequest, response, mappedHandler, 79 new NestedServletException("Handler processing failed", err)); 80 } 81 finally { 82 if (asyncManager.isConcurrentHandlingStarted()) { 83 // Instead of postHandle and afterCompletion 84 if (mappedHandler != null) { 85 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 86 } 87 } 88 else { 89 // Clean up any resources used by a multipart request. 90 if (multipartRequestParsed) { 91 cleanupMultipart(processedRequest); 92 } 93 } 94 } 95 }
结合第20行和第55行,我们可以得知:不管SpringMVC的handler类方法返回值是ModelAndView、String,也不管SpringMVC的handler类方法的入参是Map、Model、MapModel等,在SpringMVC内部都会把请求返回结果封装为一个ModelAndView。
ModelAndView实际上内部存储结构就是一个Map<String,Object>,具体请查看ModelAndView源代码
1 /* 2 * Copyright 2002-2017 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.web.servlet; 18 19 import java.util.Map; 20 21 import org.springframework.http.HttpStatus; 22 import org.springframework.lang.Nullable; 23 import org.springframework.ui.ModelMap; 24 import org.springframework.util.CollectionUtils; 25 26 /** 27 * Holder for both Model and View in the web MVC framework. 28 * Note that these are entirely distinct. This class merely holds 29 * both to make it possible for a controller to return both model 30 * and view in a single return value. 31 * 32 * <p>Represents a model and view returned by a handler, to be resolved 33 * by a DispatcherServlet. The view can take the form of a String 34 * view name which will need to be resolved by a ViewResolver object; 35 * alternatively a View object can be specified directly. The model 36 * is a Map, allowing the use of multiple objects keyed by name. 37 * 38 * @author Rod Johnson 39 * @author Juergen Hoeller 40 * @author Rob Harrop 41 * @author Rossen Stoyanchev 42 * @see DispatcherServlet 43 * @see ViewResolver 44 * @see HandlerAdapter#handle 45 * @see org.springframework.web.servlet.mvc.Controller#handleRequest 46 */ 47 public class ModelAndView { 48 49 /** View instance or view name String */ 50 @Nullable 51 private Object view; 52 53 /** Model Map */ 54 @Nullable 55 private ModelMap model; 56 57 /** Optional HTTP status for the response */ 58 @Nullable 59 private HttpStatus status; 60 61 /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */ 62 private boolean cleared = false; 63 64 65 /** 66 * Default constructor for bean-style usage: populating bean 67 * properties instead of passing in constructor arguments. 68 * @see #setView(View) 69 * @see #setViewName(String) 70 */ 71 public ModelAndView() { 72 } 73 74 /** 75 * Convenient constructor when there is no model data to expose. 76 * Can also be used in conjunction with {@code addObject}. 77 * @param viewName name of the View to render, to be resolved 78 * by the DispatcherServlet's ViewResolver 79 * @see #addObject 80 */ 81 public ModelAndView(String viewName) { 82 this.view = viewName; 83 } 84 85 /** 86 * Convenient constructor when there is no model data to expose. 87 * Can also be used in conjunction with {@code addObject}. 88 * @param view View object to render 89 * @see #addObject 90 */ 91 public ModelAndView(View view) { 92 this.view = view; 93 } 94 95 /** 96 * Create a new ModelAndView given a view name and a model. 97 * @param viewName name of the View to render, to be resolved 98 * by the DispatcherServlet's ViewResolver 99 * @param model Map of model names (Strings) to model objects 100 * (Objects). Model entries may not be {@code null}, but the 101 * model Map may be {@code null} if there is no model data. 102 */ 103 public ModelAndView(String viewName, @Nullable Map<String, ?> model) { 104 this.view = viewName; 105 if (model != null) { 106 getModelMap().addAllAttributes(model); 107 } 108 } 109 110 /** 111 * Create a new ModelAndView given a View object and a model. 112 * <emphasis>Note: the supplied model data is copied into the internal 113 * storage of this class. You should not consider to modify the supplied 114 * Map after supplying it to this class</emphasis> 115 * @param view View object to render 116 * @param model Map of model names (Strings) to model objects 117 * (Objects). Model entries may not be {@code null}, but the 118 * model Map may be {@code null} if there is no model data. 119 */ 120 public ModelAndView(View view, @Nullable Map<String, ?> model) { 121 this.view = view; 122 if (model != null) { 123 getModelMap().addAllAttributes(model); 124 } 125 } 126 127 /** 128 * Create a new ModelAndView given a view name and HTTP status. 129 * @param viewName name of the View to render, to be resolved 130 * by the DispatcherServlet's ViewResolver 131 * @param status an HTTP status code to use for the response 132 * (to be set just prior to View rendering) 133 * @since 4.3.8 134 */ 135 public ModelAndView(String viewName, HttpStatus status) { 136 this.view = viewName; 137 this.status = status; 138 } 139 140 /** 141 * Create a new ModelAndView given a view name, model, and HTTP status. 142 * @param viewName name of the View to render, to be resolved 143 * by the DispatcherServlet's ViewResolver 144 * @param model Map of model names (Strings) to model objects 145 * (Objects). Model entries may not be {@code null}, but the 146 * model Map may be {@code null} if there is no model data. 147 * @param status an HTTP status code to use for the response 148 * (to be set just prior to View rendering) 149 * @since 4.3 150 */ 151 public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) { 152 this.view = viewName; 153 if (model != null) { 154 getModelMap().addAllAttributes(model); 155 } 156 this.status = status; 157 } 158 159 /** 160 * Convenient constructor to take a single model object. 161 * @param viewName name of the View to render, to be resolved 162 * by the DispatcherServlet's ViewResolver 163 * @param modelName name of the single entry in the model 164 * @param modelObject the single model object 165 */ 166 public ModelAndView(String viewName, String modelName, Object modelObject) { 167 this.view = viewName; 168 addObject(modelName, modelObject); 169 } 170 171 /** 172 * Convenient constructor to take a single model object. 173 * @param view View object to render 174 * @param modelName name of the single entry in the model 175 * @param modelObject the single model object 176 */ 177 public ModelAndView(View view, String modelName, Object modelObject) { 178 this.view = view; 179 addObject(modelName, modelObject); 180 } 181 182 183 /** 184 * Set a view name for this ModelAndView, to be resolved by the 185 * DispatcherServlet via a ViewResolver. Will override any 186 * pre-existing view name or View. 187 */ 188 public void setViewName(@Nullable String viewName) { 189 this.view = viewName; 190 } 191 192 /** 193 * Return the view name to be resolved by the DispatcherServlet 194 * via a ViewResolver, or {@code null} if we are using a View object. 195 */ 196 @Nullable 197 public String getViewName() { 198 return (this.view instanceof String ? (String) this.view : null); 199 } 200 201 /** 202 * Set a View object for this ModelAndView. Will override any 203 * pre-existing view name or View. 204 */ 205 public void setView(@Nullable View view) { 206 this.view = view; 207 } 208 209 /** 210 * Return the View object, or {@code null} if we are using a view name 211 * to be resolved by the DispatcherServlet via a ViewResolver. 212 */ 213 @Nullable 214 public View getView() { 215 return (this.view instanceof View ? (View) this.view : null); 216 } 217 218 /** 219 * Indicate whether or not this {@code ModelAndView} has a view, either 220 * as a view name or as a direct {@link View} instance. 221 */ 222 public boolean hasView() { 223 return (this.view != null); 224 } 225 226 /** 227 * Return whether we use a view reference, i.e. {@code true} 228 * if the view has been specified via a name to be resolved by the 229 * DispatcherServlet via a ViewResolver. 230 */ 231 public boolean isReference() { 232 return (this.view instanceof String); 233 } 234 235 /** 236 * Return the model map. May return {@code null}. 237 * Called by DispatcherServlet for evaluation of the model. 238 */ 239 @Nullable 240 protected Map<String, Object> getModelInternal() { 241 return this.model; 242 } 243 244 /** 245 * Return the underlying {@code ModelMap} instance (never {@code null}). 246 */ 247 public ModelMap getModelMap() { 248 if (this.model == null) { 249 this.model = new ModelMap(); 250 } 251 return this.model; 252 } 253 254 /** 255 * Return the model map. Never returns {@code null}. 256 * To be called by application code for modifying the model. 257 */ 258 public Map<String, Object> getModel() { 259 return getModelMap(); 260 } 261 262 /** 263 * Set the HTTP status to use for the response. 264 * <p>The response status is set just prior to View rendering. 265 * @since 4.3 266 */ 267 public void setStatus(@Nullable HttpStatus status) { 268 this.status = status; 269 } 270 271 /** 272 * Return the configured HTTP status for the response, if any. 273 * @since 4.3 274 */ 275 @Nullable 276 public HttpStatus getStatus() { 277 return this.status; 278 } 279 280 281 /** 282 * Add an attribute to the model. 283 * @param attributeName name of the object to add to the model 284 * @param attributeValue object to add to the model (never {@code null}) 285 * @see ModelMap#addAttribute(String, Object) 286 * @see #getModelMap() 287 */ 288 public ModelAndView addObject(String attributeName, Object attributeValue) { 289 getModelMap().addAttribute(attributeName, attributeValue); 290 return this; 291 } 292 293 /** 294 * Add an attribute to the model using parameter name generation. 295 * @param attributeValue the object to add to the model (never {@code null}) 296 * @see ModelMap#addAttribute(Object) 297 * @see #getModelMap() 298 */ 299 public ModelAndView addObject(Object attributeValue) { 300 getModelMap().addAttribute(attributeValue); 301 return this; 302 } 303 304 /** 305 * Add all attributes contained in the provided Map to the model. 306 * @param modelMap a Map of attributeName -> attributeValue pairs 307 * @see ModelMap#addAllAttributes(Map) 308 * @see #getModelMap() 309 */ 310 public ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap) { 311 getModelMap().addAllAttributes(modelMap); 312 return this; 313 } 314 315 316 /** 317 * Clear the state of this ModelAndView object. 318 * The object will be empty afterwards. 319 * <p>Can be used to suppress rendering of a given ModelAndView object 320 * in the {@code postHandle} method of a HandlerInterceptor. 321 * @see #isEmpty() 322 * @see HandlerInterceptor#postHandle 323 */ 324 public void clear() { 325 this.view = null; 326 this.model = null; 327 this.cleared = true; 328 } 329 330 /** 331 * Return whether this ModelAndView object is empty, 332 * i.e. whether it does not hold any view and does not contain a model. 333 */ 334 public boolean isEmpty() { 335 return (this.view == null && CollectionUtils.isEmpty(this.model)); 336 } 337 338 /** 339 * Return whether this ModelAndView object is empty as a result of a call to {@link #clear} 340 * i.e. whether it does not hold any view and does not contain a model. 341 * <p>Returns {@code false} if any additional state was added to the instance 342 * <strong>after</strong> the call to {@link #clear}. 343 * @see #clear() 344 */ 345 public boolean wasCleared() { 346 return (this.cleared && isEmpty()); 347 } 348 349 350 /** 351 * Return diagnostic information about this model and view. 352 */ 353 @Override 354 public String toString() { 355 StringBuilder sb = new StringBuilder("ModelAndView: "); 356 if (isReference()) { 357 sb.append("reference to view with name '").append(this.view).append("'"); 358 } 359 else { 360 sb.append("materialized View is [").append(this.view).append(']'); 361 } 362 sb.append("; model is ").append(this.model); 363 return sb.toString(); 364 } 365 366 }