001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet; 018 019import java.util.Map; 020 021import org.springframework.http.HttpStatus; 022import org.springframework.ui.ModelMap; 023import org.springframework.util.CollectionUtils; 024 025/** 026 * Holder for both Model and View in the web MVC framework. 027 * Note that these are entirely distinct. This class merely holds 028 * both to make it possible for a controller to return both model 029 * and view in a single return value. 030 * 031 * <p>Represents a model and view returned by a handler, to be resolved 032 * by a DispatcherServlet. The view can take the form of a String 033 * view name which will need to be resolved by a ViewResolver object; 034 * alternatively a View object can be specified directly. The model 035 * is a Map, allowing the use of multiple objects keyed by name. 036 * 037 * @author Rod Johnson 038 * @author Juergen Hoeller 039 * @author Rob Harrop 040 * @author Rossen Stoyanchev 041 * @see DispatcherServlet 042 * @see ViewResolver 043 * @see HandlerAdapter#handle 044 * @see org.springframework.web.servlet.mvc.Controller#handleRequest 045 */ 046public class ModelAndView { 047 048 /** View instance or view name String */ 049 private Object view; 050 051 /** Model Map */ 052 private ModelMap model; 053 054 /** Optional HTTP status for the response */ 055 private HttpStatus status; 056 057 /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */ 058 private boolean cleared = false; 059 060 061 /** 062 * Default constructor for bean-style usage: populating bean 063 * properties instead of passing in constructor arguments. 064 * @see #setView(View) 065 * @see #setViewName(String) 066 */ 067 public ModelAndView() { 068 } 069 070 /** 071 * Convenient constructor when there is no model data to expose. 072 * Can also be used in conjunction with {@code addObject}. 073 * @param viewName name of the View to render, to be resolved 074 * by the DispatcherServlet's ViewResolver 075 * @see #addObject 076 */ 077 public ModelAndView(String viewName) { 078 this.view = viewName; 079 } 080 081 /** 082 * Convenient constructor when there is no model data to expose. 083 * Can also be used in conjunction with {@code addObject}. 084 * @param view View object to render 085 * @see #addObject 086 */ 087 public ModelAndView(View view) { 088 this.view = view; 089 } 090 091 /** 092 * Create a new ModelAndView given a view name and a model. 093 * @param viewName name of the View to render, to be resolved 094 * by the DispatcherServlet's ViewResolver 095 * @param model Map of model names (Strings) to model objects 096 * (Objects). Model entries may not be {@code null}, but the 097 * model Map may be {@code null} if there is no model data. 098 */ 099 public ModelAndView(String viewName, Map<String, ?> model) { 100 this.view = viewName; 101 if (model != null) { 102 getModelMap().addAllAttributes(model); 103 } 104 } 105 106 /** 107 * Create a new ModelAndView given a View object and a model. 108 * <emphasis>Note: the supplied model data is copied into the internal 109 * storage of this class. You should not consider to modify the supplied 110 * Map after supplying it to this class</emphasis> 111 * @param view View object to render 112 * @param model Map of model names (Strings) to model objects 113 * (Objects). Model entries may not be {@code null}, but the 114 * model Map may be {@code null} if there is no model data. 115 */ 116 public ModelAndView(View view, Map<String, ?> model) { 117 this.view = view; 118 if (model != null) { 119 getModelMap().addAllAttributes(model); 120 } 121 } 122 123 /** 124 * Create a new ModelAndView given a view name and HTTP status. 125 * @param viewName name of the View to render, to be resolved 126 * by the DispatcherServlet's ViewResolver 127 * @param status an HTTP status code to use for the response 128 * (to be set just prior to View rendering) 129 * @since 4.3.8 130 */ 131 public ModelAndView(String viewName, HttpStatus status) { 132 this.view = viewName; 133 this.status = status; 134 } 135 136 /** 137 * Create a new ModelAndView given a view name, model, and HTTP status. 138 * @param viewName name of the View to render, to be resolved 139 * by the DispatcherServlet's ViewResolver 140 * @param model Map of model names (Strings) to model objects 141 * (Objects). Model entries may not be {@code null}, but the 142 * model Map may be {@code null} if there is no model data. 143 * @param status an HTTP status code to use for the response 144 * (to be set just prior to View rendering) 145 * @since 4.3 146 */ 147 public ModelAndView(String viewName, Map<String, ?> model, HttpStatus status) { 148 this.view = viewName; 149 if (model != null) { 150 getModelMap().addAllAttributes(model); 151 } 152 this.status = status; 153 } 154 155 /** 156 * Convenient constructor to take a single model object. 157 * @param viewName name of the View to render, to be resolved 158 * by the DispatcherServlet's ViewResolver 159 * @param modelName name of the single entry in the model 160 * @param modelObject the single model object 161 */ 162 public ModelAndView(String viewName, String modelName, Object modelObject) { 163 this.view = viewName; 164 addObject(modelName, modelObject); 165 } 166 167 /** 168 * Convenient constructor to take a single model object. 169 * @param view View object to render 170 * @param modelName name of the single entry in the model 171 * @param modelObject the single model object 172 */ 173 public ModelAndView(View view, String modelName, Object modelObject) { 174 this.view = view; 175 addObject(modelName, modelObject); 176 } 177 178 179 /** 180 * Set a view name for this ModelAndView, to be resolved by the 181 * DispatcherServlet via a ViewResolver. Will override any 182 * pre-existing view name or View. 183 */ 184 public void setViewName(String viewName) { 185 this.view = viewName; 186 } 187 188 /** 189 * Return the view name to be resolved by the DispatcherServlet 190 * via a ViewResolver, or {@code null} if we are using a View object. 191 */ 192 public String getViewName() { 193 return (this.view instanceof String ? (String) this.view : null); 194 } 195 196 /** 197 * Set a View object for this ModelAndView. Will override any 198 * pre-existing view name or View. 199 */ 200 public void setView(View view) { 201 this.view = view; 202 } 203 204 /** 205 * Return the View object, or {@code null} if we are using a view name 206 * to be resolved by the DispatcherServlet via a ViewResolver. 207 */ 208 public View getView() { 209 return (this.view instanceof View ? (View) this.view : null); 210 } 211 212 /** 213 * Indicate whether or not this {@code ModelAndView} has a view, either 214 * as a view name or as a direct {@link View} instance. 215 */ 216 public boolean hasView() { 217 return (this.view != null); 218 } 219 220 /** 221 * Return whether we use a view reference, i.e. {@code true} 222 * if the view has been specified via a name to be resolved by the 223 * DispatcherServlet via a ViewResolver. 224 */ 225 public boolean isReference() { 226 return (this.view instanceof String); 227 } 228 229 /** 230 * Return the model map. May return {@code null}. 231 * Called by DispatcherServlet for evaluation of the model. 232 */ 233 protected Map<String, Object> getModelInternal() { 234 return this.model; 235 } 236 237 /** 238 * Return the underlying {@code ModelMap} instance (never {@code null}). 239 */ 240 public ModelMap getModelMap() { 241 if (this.model == null) { 242 this.model = new ModelMap(); 243 } 244 return this.model; 245 } 246 247 /** 248 * Return the model map. Never returns {@code null}. 249 * To be called by application code for modifying the model. 250 */ 251 public Map<String, Object> getModel() { 252 return getModelMap(); 253 } 254 255 /** 256 * Set the HTTP status to use for the response. 257 * <p>The response status is set just prior to View rendering. 258 * @since 4.3 259 */ 260 public void setStatus(HttpStatus status) { 261 this.status = status; 262 } 263 264 /** 265 * Return the configured HTTP status for the response, if any. 266 * @since 4.3 267 */ 268 public HttpStatus getStatus() { 269 return this.status; 270 } 271 272 273 /** 274 * Add an attribute to the model. 275 * @param attributeName name of the object to add to the model 276 * @param attributeValue object to add to the model (never {@code null}) 277 * @see ModelMap#addAttribute(String, Object) 278 * @see #getModelMap() 279 */ 280 public ModelAndView addObject(String attributeName, Object attributeValue) { 281 getModelMap().addAttribute(attributeName, attributeValue); 282 return this; 283 } 284 285 /** 286 * Add an attribute to the model using parameter name generation. 287 * @param attributeValue the object to add to the model (never {@code null}) 288 * @see ModelMap#addAttribute(Object) 289 * @see #getModelMap() 290 */ 291 public ModelAndView addObject(Object attributeValue) { 292 getModelMap().addAttribute(attributeValue); 293 return this; 294 } 295 296 /** 297 * Add all attributes contained in the provided Map to the model. 298 * @param modelMap a Map of attributeName -> attributeValue pairs 299 * @see ModelMap#addAllAttributes(Map) 300 * @see #getModelMap() 301 */ 302 public ModelAndView addAllObjects(Map<String, ?> modelMap) { 303 getModelMap().addAllAttributes(modelMap); 304 return this; 305 } 306 307 308 /** 309 * Clear the state of this ModelAndView object. 310 * The object will be empty afterwards. 311 * <p>Can be used to suppress rendering of a given ModelAndView object 312 * in the {@code postHandle} method of a HandlerInterceptor. 313 * @see #isEmpty() 314 * @see HandlerInterceptor#postHandle 315 */ 316 public void clear() { 317 this.view = null; 318 this.model = null; 319 this.cleared = true; 320 } 321 322 /** 323 * Return whether this ModelAndView object is empty, 324 * i.e. whether it does not hold any view and does not contain a model. 325 */ 326 public boolean isEmpty() { 327 return (this.view == null && CollectionUtils.isEmpty(this.model)); 328 } 329 330 /** 331 * Return whether this ModelAndView object is empty as a result of a call to {@link #clear} 332 * i.e. whether it does not hold any view and does not contain a model. 333 * <p>Returns {@code false} if any additional state was added to the instance 334 * <strong>after</strong> the call to {@link #clear}. 335 * @see #clear() 336 */ 337 public boolean wasCleared() { 338 return (this.cleared && isEmpty()); 339 } 340 341 342 /** 343 * Return diagnostic information about this model and view. 344 */ 345 @Override 346 public String toString() { 347 StringBuilder sb = new StringBuilder("ModelAndView: "); 348 if (isReference()) { 349 sb.append("reference to view with name '").append(this.view).append("'"); 350 } 351 else { 352 sb.append("materialized View is [").append(this.view).append(']'); 353 } 354 sb.append("; model is ").append(this.model); 355 return sb.toString(); 356 } 357 358}