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>

点击链接地址,显示结果:

SpringMVC(九):SpringMVC 处理输出模型数据之ModelAndView

对TestModelAndView.java中“ modelAndView.addObject("currentTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));”该行添加断点,进行调试:

根据调试信息,索引到DispatcherServlet的doDispatcher方法中:

SpringMVC(九):SpringMVC 处理输出模型数据之ModelAndView

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 }
View Code

相关文章: