001/* 002 * Copyright 2002-2018 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.lang.Nullable; 023import org.springframework.ui.ModelMap; 024import org.springframework.util.CollectionUtils; 025 026/** 027 * Holder for both Model and View in the web MVC framework. 028 * Note that these are entirely distinct. This class merely holds 029 * both to make it possible for a controller to return both model 030 * and view in a single return value. 031 * 032 * <p>Represents a model and view returned by a handler, to be resolved 033 * by a DispatcherServlet. The view can take the form of a String 034 * view name which will need to be resolved by a ViewResolver object; 035 * alternatively a View object can be specified directly. The model 036 * is a Map, allowing the use of multiple objects keyed by name. 037 * 038 * @author Rod Johnson 039 * @author Juergen Hoeller 040 * @author Rob Harrop 041 * @author Rossen Stoyanchev 042 * @see DispatcherServlet 043 * @see ViewResolver 044 * @see HandlerAdapter#handle 045 * @see org.springframework.web.servlet.mvc.Controller#handleRequest 046 */ 047public class ModelAndView { 048 049 /** View instance or view name String. */ 050 @Nullable 051 private Object view; 052 053 /** Model Map. */ 054 @Nullable 055 private ModelMap model; 056 057 /** Optional HTTP status for the response. */ 058 @Nullable 059 private HttpStatus status; 060 061 /** Indicates whether or not this instance has been cleared with a call to {@link #clear()}. */ 062 private boolean cleared = false; 063 064 065 /** 066 * Default constructor for bean-style usage: populating bean 067 * properties instead of passing in constructor arguments. 068 * @see #setView(View) 069 * @see #setViewName(String) 070 */ 071 public ModelAndView() { 072 } 073 074 /** 075 * Convenient constructor when there is no model data to expose. 076 * Can also be used in conjunction with {@code addObject}. 077 * @param viewName name of the View to render, to be resolved 078 * by the DispatcherServlet's ViewResolver 079 * @see #addObject 080 */ 081 public ModelAndView(String viewName) { 082 this.view = viewName; 083 } 084 085 /** 086 * Convenient constructor when there is no model data to expose. 087 * Can also be used in conjunction with {@code addObject}. 088 * @param view the View object to render 089 * @see #addObject 090 */ 091 public ModelAndView(View view) { 092 this.view = view; 093 } 094 095 /** 096 * Create a new ModelAndView given a view name and a model. 097 * @param viewName name of the View to render, to be resolved 098 * by the DispatcherServlet's ViewResolver 099 * @param model a 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 * <em>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</em> 115 * @param view the View object to render 116 * @param model a 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 a 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 the 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 (never {@code null}) 284 * @param attributeValue object to add to the model (can be {@code null}) 285 * @see ModelMap#addAttribute(String, Object) 286 * @see #getModelMap() 287 */ 288 public ModelAndView addObject(String attributeName, @Nullable 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 return "ModelAndView [view=" + formatView() + "; model=" + this.model + "]"; 356 } 357 358 private String formatView() { 359 return isReference() ? "\"" + this.view + "\"" : "[" + this.view + "]"; 360 } 361 362}