博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC(九):SpringMVC 处理输出模型数据之ModelAndView
阅读量:5137 次
发布时间:2019-06-13

本文共 61733 字,大约阅读时间需要 205 分钟。

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;@Controllerpublic 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,添加链接:

修改/WEB-INF/views/success.jsp,编辑添加内容:

current time:${requestScope.currentTime}    

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

对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      * 

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 *

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 response10 * @throws Exception in case of any kind of processing failure11 */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 afterCompletion84 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  * 

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 objects100 * (Objects). Model entries may not be {
@code null}, but the101 * model Map may be {
@code null} if there is no model data.102 */103 public ModelAndView(String viewName, @Nullable Map
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 *
Note: the supplied model data is copied into the internal113 * storage of this class. You should not consider to modify the supplied114 * Map after supplying it to this class
115 * @param view View object to render116 * @param model Map of model names (Strings) to model objects117 * (Objects). Model entries may not be {
@code null}, but the118 * model Map may be {
@code null} if there is no model data.119 */120 public ModelAndView(View view, @Nullable Map
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 resolved130 * by the DispatcherServlet's ViewResolver131 * @param status an HTTP status code to use for the response132 * (to be set just prior to View rendering)133 * @since 4.3.8134 */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 resolved143 * by the DispatcherServlet's ViewResolver144 * @param model Map of model names (Strings) to model objects145 * (Objects). Model entries may not be {
@code null}, but the146 * model Map may be {
@code null} if there is no model data.147 * @param status an HTTP status code to use for the response148 * (to be set just prior to View rendering)149 * @since 4.3150 */151 public ModelAndView(@Nullable String viewName, @Nullable Map
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 resolved162 * by the DispatcherServlet's ViewResolver163 * @param modelName name of the single entry in the model164 * @param modelObject the single model object165 */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 render174 * @param modelName name of the single entry in the model175 * @param modelObject the single model object176 */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 the185 * DispatcherServlet via a ViewResolver. Will override any186 * 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 DispatcherServlet194 * via a ViewResolver, or {
@code null} if we are using a View object.195 */196 @Nullable197 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 any203 * 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 name211 * to be resolved by the DispatcherServlet via a ViewResolver.212 */213 @Nullable214 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, either220 * 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 the229 * 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 @Nullable240 protected Map
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
getModel() {259 return getModelMap();260 }261 262 /**263 * Set the HTTP status to use for the response.264 *

The response status is set just prior to View rendering.265 * @since 4.3266 */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.3274 */275 @Nullable276 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 model284 * @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 pairs307 * @see ModelMap#addAllAttributes(Map)308 * @see #getModelMap()309 */310 public ModelAndView addAllObjects(@Nullable Map
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 *

Can be used to suppress rendering of a given ModelAndView object320 * in the {

@code postHandle} method of a HandlerInterceptor.321 * @see #isEmpty()322 * @see HandlerInterceptor#postHandle323 */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 *

Returns {

@code false} if any additional state was added to the instance342 * after 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 @Override354 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

、ModelMap源代码

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.ui; 18  19 import java.util.Collection; 20 import java.util.LinkedHashMap; 21 import java.util.Map; 22  23 import org.springframework.core.Conventions; 24 import org.springframework.lang.Nullable; 25 import org.springframework.util.Assert; 26  27 /** 28  * Implementation of {
@link java.util.Map} for use when building model data for use 29 * with UI tools. Supports chained calls and generation of model attribute names. 30 * 31 *

This class serves as generic model holder for Servlet MVC but is not tied to it. 32 * Check out the {

@link Model} interface for an interface variant. 33 * 34 * @author Rob Harrop 35 * @author Juergen Hoeller 36 * @since 2.0 37 * @see Conventions#getVariableName 38 * @see org.springframework.web.servlet.ModelAndView 39 */ 40 @SuppressWarnings("serial") 41 public class ModelMap extends LinkedHashMap
{ 42 43 /** 44 * Construct a new, empty {
@code ModelMap}. 45 */ 46 public ModelMap() { 47 } 48 49 /** 50 * Construct a new {
@code ModelMap} containing the supplied attribute 51 * under the supplied name. 52 * @see #addAttribute(String, Object) 53 */ 54 public ModelMap(String attributeName, Object attributeValue) { 55 addAttribute(attributeName, attributeValue); 56 } 57 58 /** 59 * Construct a new {
@code ModelMap} containing the supplied attribute. 60 * Uses attribute name generation to generate the key for the supplied model 61 * object. 62 * @see #addAttribute(Object) 63 */ 64 public ModelMap(Object attributeValue) { 65 addAttribute(attributeValue); 66 } 67 68 69 /** 70 * Add the supplied attribute under the supplied name. 71 * @param attributeName the name of the model attribute (never {
@code null}) 72 * @param attributeValue the model attribute value (can be {
@code null}) 73 */ 74 public ModelMap addAttribute(String attributeName, @Nullable Object attributeValue) { 75 Assert.notNull(attributeName, "Model attribute name must not be null"); 76 put(attributeName, attributeValue); 77 return this; 78 } 79 80 /** 81 * Add the supplied attribute to this {
@code Map} using a 82 * {
@link org.springframework.core.Conventions#getVariableName generated name}. 83 *

Note: Empty {
@link Collection Collections} are not added to 84 * the model when using this method because we cannot correctly determine 85 * the true convention name. View code should check for {
@code null} rather 86 * than for empty collections as is already done by JSTL tags.
87 * @param attributeValue the model attribute value (never {
@code null}) 88 */ 89 public ModelMap addAttribute(Object attributeValue) { 90 Assert.notNull(attributeValue, "Model object must not be null"); 91 if (attributeValue instanceof Collection && ((Collection
) attributeValue).isEmpty()) { 92 return this; 93 } 94 return addAttribute(Conventions.getVariableName(attributeValue), attributeValue); 95 } 96 97 /** 98 * Copy all attributes in the supplied {
@code Collection} into this 99 * {
@code Map}, using attribute name generation for each element.100 * @see #addAttribute(Object)101 */102 public ModelMap addAllAttributes(@Nullable Collection
attributeValues) {103 if (attributeValues != null) {104 for (Object attributeValue : attributeValues) {105 addAttribute(attributeValue);106 }107 }108 return this;109 }110 111 /**112 * Copy all attributes in the supplied {
@code Map} into this {
@code Map}.113 * @see #addAttribute(String, Object)114 */115 public ModelMap addAllAttributes(@Nullable Map
attributes) {116 if (attributes != null) {117 putAll(attributes);118 }119 return this;120 }121 122 /**123 * Copy all attributes in the supplied {
@code Map} into this {
@code Map},124 * with existing objects of the same name taking precedence (i.e. not getting125 * replaced).126 */127 public ModelMap mergeAttributes(@Nullable Map
attributes) {128 if (attributes != null) {129 attributes.forEach((key, value) -> {130 if (!containsKey(key)) {131 put(key, value);132 }133 });134 }135 return this;136 }137 138 /**139 * Does this model contain an attribute of the given name?140 * @param attributeName the name of the model attribute (never {
@code null})141 * @return whether this model contains a corresponding attribute142 */143 public boolean containsAttribute(String attributeName) {144 return containsKey(attributeName);145 }146 147 }

View Code

、LinkedHashMap源代码

1 /*  2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.  3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.  4  *  5  *  6  *  7  *  8  *  9  * 10  * 11  * 12  * 13  * 14  * 15  * 16  * 17  * 18  * 19  * 20  * 21  * 22  * 23  * 24  */ 25  26 package java.util; 27  28 import java.util.function.Consumer; 29 import java.util.function.BiConsumer; 30 import java.util.function.BiFunction; 31 import java.io.IOException; 32  33 /** 34  * 

Hash table and linked list implementation of the Map interface, 35 * with predictable iteration order. This implementation differs from 36 * HashMap in that it maintains a doubly-linked list running through 37 * all of its entries. This linked list defines the iteration ordering, 38 * which is normally the order in which keys were inserted into the map 39 * (insertion-order). Note that insertion order is not affected 40 * if a key is re-inserted into the map. (A key k is 41 * reinserted into a map m if m.put(k, v) is invoked when 42 * m.containsKey(k) would return true immediately prior to 43 * the invocation.) 44 * 45 *

This implementation spares its clients from the unspecified, generally 46 * chaotic ordering provided by {

@link HashMap} (and {
@link Hashtable}), 47 * without incurring the increased cost associated with {
@link TreeMap}. It 48 * can be used to produce a copy of a map that has the same order as the 49 * original, regardless of the original map's implementation: 50 *

 51  *     void foo(Map m) { 52  *         Map copy = new LinkedHashMap(m); 53  *         ... 54  *     } 55  * 
56 * This technique is particularly useful if a module takes a map on input, 57 * copies it, and later returns results whose order is determined by that of 58 * the copy. (Clients generally appreciate having things returned in the same 59 * order they were presented.) 60 * 61 *

A special {

@link #LinkedHashMap(int,float,boolean) constructor} is 62 * provided to create a linked hash map whose order of iteration is the order 63 * in which its entries were last accessed, from least-recently accessed to 64 * most-recently (access-order). This kind of map is well-suited to 65 * building LRU caches. Invoking the {
@code put}, {
@code putIfAbsent}, 66 * {
@code get}, {
@code getOrDefault}, {
@code compute}, {
@code computeIfAbsent}, 67 * {
@code computeIfPresent}, or {
@code merge} methods results 68 * in an access to the corresponding entry (assuming it exists after the 69 * invocation completes). The {
@code replace} methods only result in an access 70 * of the entry if the value is replaced. The {
@code putAll} method generates one 71 * entry access for each mapping in the specified map, in the order that 72 * key-value mappings are provided by the specified map's entry set iterator. 73 * No other methods generate entry accesses. In particular, operations 74 * on collection-views do not affect the order of iteration of the 75 * backing map. 76 * 77 *

The {

@link #removeEldestEntry(Map.Entry)} method may be overridden to 78 * impose a policy for removing stale mappings automatically when new mappings 79 * are added to the map. 80 * 81 *

This class provides all of the optional Map operations, and 82 * permits null elements. Like HashMap, it provides constant-time 83 * performance for the basic operations (add, contains and 84 * remove), assuming the hash function disperses elements 85 * properly among the buckets. Performance is likely to be just slightly 86 * below that of HashMap, due to the added expense of maintaining the 87 * linked list, with one exception: Iteration over the collection-views 88 * of a LinkedHashMap requires time proportional to the size 89 * of the map, regardless of its capacity. Iteration over a HashMap 90 * is likely to be more expensive, requiring time proportional to its 91 * capacity. 92 * 93 *

A linked hash map has two parameters that affect its performance: 94 * initial capacity and load factor. They are defined precisely 95 * as for HashMap. Note, however, that the penalty for choosing an 96 * excessively high value for initial capacity is less severe for this class 97 * than for HashMap, as iteration times for this class are unaffected 98 * by capacity. 99 *100 *

Note that this implementation is not synchronized.101 * If multiple threads access a linked hash map concurrently, and at least102 * one of the threads modifies the map structurally, it must be103 * synchronized externally. This is typically accomplished by104 * synchronizing on some object that naturally encapsulates the map.105 *106 * If no such object exists, the map should be "wrapped" using the107 * {

@link Collections#synchronizedMap Collections.synchronizedMap}108 * method. This is best done at creation time, to prevent accidental109 * unsynchronized access to the map:

110  *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));
111 *112 * A structural modification is any operation that adds or deletes one or more113 * mappings or, in the case of access-ordered linked hash maps, affects114 * iteration order. In insertion-ordered linked hash maps, merely changing115 * the value associated with a key that is already contained in the map is not116 * a structural modification. In access-ordered linked hash maps,117 * merely querying the map with get is a structural modification.118 * )119 *120 *

The iterators returned by the iterator method of the collections121 * returned by all of this class's collection view methods are122 * fail-fast: if the map is structurally modified at any time after123 * the iterator is created, in any way except through the iterator's own124 * remove method, the iterator will throw a {

@link125 * ConcurrentModificationException}. Thus, in the face of concurrent126 * modification, the iterator fails quickly and cleanly, rather than risking127 * arbitrary, non-deterministic behavior at an undetermined time in the future.128 *129 *

Note that the fail-fast behavior of an iterator cannot be guaranteed130 * as it is, generally speaking, impossible to make any hard guarantees in the131 * presence of unsynchronized concurrent modification. Fail-fast iterators132 * throw ConcurrentModificationException on a best-effort basis.133 * Therefore, it would be wrong to write a program that depended on this134 * exception for its correctness: the fail-fast behavior of iterators135 * should be used only to detect bugs.136 *137 *

The spliterators returned by the spliterator method of the collections138 * returned by all of this class's collection view methods are139 * late-binding,140 * fail-fast, and additionally report {

@link Spliterator#ORDERED}.141 *142 *

This class is a member of the143 * 144 * Java Collections Framework.145 *146 * @implNote147 * The spliterators returned by the spliterator method of the collections148 * returned by all of this class's collection view methods are created from149 * the iterators of the corresponding collections.150 *151 * @param

the type of keys maintained by this map152 * @param
the type of mapped values153 *154 * @author Josh Bloch155 * @see Object#hashCode()156 * @see Collection157 * @see Map158 * @see HashMap159 * @see TreeMap160 * @see Hashtable161 * @since 1.4162 */163 public class LinkedHashMap
164 extends HashMap
165 implements Map
166 {167 168 /*169 * Implementation note. A previous version of this class was170 * internally structured a little differently. Because superclass171 * HashMap now uses trees for some of its nodes, class172 * LinkedHashMap.Entry is now treated as intermediary node class173 * that can also be converted to tree form. The name of this174 * class, LinkedHashMap.Entry, is confusing in several ways in its175 * current context, but cannot be changed. Otherwise, even though176 * it is not exported outside this package, some existing source177 * code is known to have relied on a symbol resolution corner case178 * rule in calls to removeEldestEntry that suppressed compilation179 * errors due to ambiguous usages. So, we keep the name to180 * preserve unmodified compilability.181 *182 * The changes in node classes also require using two fields183 * (head, tail) rather than a pointer to a header node to maintain184 * the doubly-linked before/after list. This class also185 * previously used a different style of callback methods upon186 * access, insertion, and removal.187 */188 189 /**190 * HashMap.Node subclass for normal LinkedHashMap entries.191 */192 static class Entry
extends HashMap.Node
{193 Entry
before, after;194 Entry(int hash, K key, V value, Node
next) {195 super(hash, key, value, next);196 }197 }198 199 private static final long serialVersionUID = 3801124242820219131L;200 201 /**202 * The head (eldest) of the doubly linked list.203 */204 transient LinkedHashMap.Entry
head;205 206 /**207 * The tail (youngest) of the doubly linked list.208 */209 transient LinkedHashMap.Entry
tail;210 211 /**212 * The iteration ordering method for this linked hash map:
true213 * for access-order,
false for insertion-order.214 *215 * @serial216 */217 final boolean accessOrder;218 219 // internal utilities220 221 // link at the end of list222 private void linkNodeLast(LinkedHashMap.Entry
p) {223 LinkedHashMap.Entry
last = tail;224 tail = p;225 if (last == null)226 head = p;227 else {228 p.before = last;229 last.after = p;230 }231 }232 233 // apply src's links to dst234 private void transferLinks(LinkedHashMap.Entry
src,235 LinkedHashMap.Entry
dst) {236 LinkedHashMap.Entry
b = dst.before = src.before;237 LinkedHashMap.Entry
a = dst.after = src.after;238 if (b == null)239 head = dst;240 else241 b.after = dst;242 if (a == null)243 tail = dst;244 else245 a.before = dst;246 }247 248 // overrides of HashMap hook methods249 250 void reinitialize() {251 super.reinitialize();252 head = tail = null;253 }254 255 Node
newNode(int hash, K key, V value, Node
e) {256 LinkedHashMap.Entry
p =257 new LinkedHashMap.Entry
(hash, key, value, e);258 linkNodeLast(p);259 return p;260 }261 262 Node
replacementNode(Node
p, Node
next) {263 LinkedHashMap.Entry
q = (LinkedHashMap.Entry
)p;264 LinkedHashMap.Entry
t =265 new LinkedHashMap.Entry
(q.hash, q.key, q.value, next);266 transferLinks(q, t);267 return t;268 }269 270 TreeNode
newTreeNode(int hash, K key, V value, Node
next) {271 TreeNode
p = new TreeNode
(hash, key, value, next);272 linkNodeLast(p);273 return p;274 }275 276 TreeNode
replacementTreeNode(Node
p, Node
next) {277 LinkedHashMap.Entry
q = (LinkedHashMap.Entry
)p;278 TreeNode
t = new TreeNode
(q.hash, q.key, q.value, next);279 transferLinks(q, t);280 return t;281 }282 283 void afterNodeRemoval(Node
e) { // unlink284 LinkedHashMap.Entry
p =285 (LinkedHashMap.Entry
)e, b = p.before, a = p.after;286 p.before = p.after = null;287 if (b == null)288 head = a;289 else290 b.after = a;291 if (a == null)292 tail = b;293 else294 a.before = b;295 }296 297 void afterNodeInsertion(boolean evict) { // possibly remove eldest298 LinkedHashMap.Entry
first;299 if (evict && (first = head) != null && removeEldestEntry(first)) {300 K key = first.key;301 removeNode(hash(key), key, null, false, true);302 }303 }304 305 void afterNodeAccess(Node
e) { // move node to last306 LinkedHashMap.Entry
last;307 if (accessOrder && (last = tail) != e) {308 LinkedHashMap.Entry
p =309 (LinkedHashMap.Entry
)e, b = p.before, a = p.after;310 p.after = null;311 if (b == null)312 head = a;313 else314 b.after = a;315 if (a != null)316 a.before = b;317 else318 last = b;319 if (last == null)320 head = p;321 else {322 p.before = last;323 last.after = p;324 }325 tail = p;326 ++modCount;327 }328 }329 330 void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {331 for (LinkedHashMap.Entry
e = head; e != null; e = e.after) {332 s.writeObject(e.key);333 s.writeObject(e.value);334 }335 }336 337 /**338 * Constructs an empty insertion-ordered
LinkedHashMap instance339 * with the specified initial capacity and load factor.340 *341 * @param initialCapacity the initial capacity342 * @param loadFactor the load factor343 * @throws IllegalArgumentException if the initial capacity is negative344 * or the load factor is nonpositive345 */346 public LinkedHashMap(int initialCapacity, float loadFactor) {347 super(initialCapacity, loadFactor);348 accessOrder = false;349 }350 351 /**352 * Constructs an empty insertion-ordered
LinkedHashMap instance353 * with the specified initial capacity and a default load factor (0.75).354 *355 * @param initialCapacity the initial capacity356 * @throws IllegalArgumentException if the initial capacity is negative357 */358 public LinkedHashMap(int initialCapacity) {359 super(initialCapacity);360 accessOrder = false;361 }362 363 /**364 * Constructs an empty insertion-ordered
LinkedHashMap instance365 * with the default initial capacity (16) and load factor (0.75).366 */367 public LinkedHashMap() {368 super();369 accessOrder = false;370 }371 372 /**373 * Constructs an insertion-ordered
LinkedHashMap instance with374 * the same mappings as the specified map. The
LinkedHashMap375 * instance is created with a default load factor (0.75) and an initial376 * capacity sufficient to hold the mappings in the specified map.377 *378 * @param m the map whose mappings are to be placed in this map379 * @throws NullPointerException if the specified map is null380 */381 public LinkedHashMap(Map
m) {382 super();383 accessOrder = false;384 putMapEntries(m, false);385 }386 387 /**388 * Constructs an empty
LinkedHashMap instance with the389 * specified initial capacity, load factor and ordering mode.390 *391 * @param initialCapacity the initial capacity392 * @param loadFactor the load factor393 * @param accessOrder the ordering mode -
true for394 * access-order,
false for insertion-order395 * @throws IllegalArgumentException if the initial capacity is negative396 * or the load factor is nonpositive397 */398 public LinkedHashMap(int initialCapacity,399 float loadFactor,400 boolean accessOrder) {401 super(initialCapacity, loadFactor);402 this.accessOrder = accessOrder;403 }404 405 406 /**407 * Returns
true if this map maps one or more keys to the408 * specified value.409 *410 * @param value value whose presence in this map is to be tested411 * @return
true if this map maps one or more keys to the412 * specified value413 */414 public boolean containsValue(Object value) {415 for (LinkedHashMap.Entry
e = head; e != null; e = e.after) {416 V v = e.value;417 if (v == value || (value != null && value.equals(v)))418 return true;419 }420 return false;421 }422 423 /**424 * Returns the value to which the specified key is mapped,425 * or { @code null} if this map contains no mapping for the key.426 *427 *

More formally, if this map contains a mapping from a key428 * {

@code k} to a value {
@code v} such that {
@code (key==null ? k==null :429 * key.equals(k))}, then this method returns {
@code v}; otherwise430 * it returns {
@code null}. (There can be at most one such mapping.)431 *432 *

A return value of {

@code null} does not necessarily433 * indicate that the map contains no mapping for the key; it's also434 * possible that the map explicitly maps the key to {
@code null}.435 * The {
@link #containsKey containsKey} operation may be used to436 * distinguish these two cases.437 */438 public V get(Object key) {439 Node
e;440 if ((e = getNode(hash(key), key)) == null)441 return null;442 if (accessOrder)443 afterNodeAccess(e);444 return e.value;445 }446 447 /**448 * {
@inheritDoc}449 */450 public V getOrDefault(Object key, V defaultValue) {451 Node
e;452 if ((e = getNode(hash(key), key)) == null)453 return defaultValue;454 if (accessOrder)455 afterNodeAccess(e);456 return e.value;457 }458 459 /**460 * {
@inheritDoc}461 */462 public void clear() {463 super.clear();464 head = tail = null;465 }466 467 /**468 * Returns
true if this map should remove its eldest entry.469 * This method is invoked by
put and
putAll after470 * inserting a new entry into the map. It provides the implementor471 * with the opportunity to remove the eldest entry each time a new one472 * is added. This is useful if the map represents a cache: it allows473 * the map to reduce memory consumption by deleting stale entries.474 *475 *

Sample use: this override will allow the map to grow up to 100476 * entries and then delete the eldest entry each time a new entry is477 * added, maintaining a steady state of 100 entries.478 *

479      *     private static final int MAX_ENTRIES = 100;480      *481      *     protected boolean removeEldestEntry(Map.Entry eldest) {482      *        return size() > MAX_ENTRIES;483      *     }484      * 
485 *486 *

This method typically does not modify the map in any way,487 * instead allowing the map to modify itself as directed by its488 * return value. It is permitted for this method to modify489 * the map directly, but if it does so, it must return490 * false (indicating that the map should not attempt any491 * further modification). The effects of returning true492 * after modifying the map from within this method are unspecified.493 *494 *

This implementation merely returns false (so that this495 * map acts like a normal map - the eldest element is never removed).496 *497 * @param eldest The least recently inserted entry in the map, or if498 * this is an access-ordered map, the least recently accessed499 * entry. This is the entry that will be removed it this500 * method returns true. If the map was empty prior501 * to the put or putAll invocation resulting502 * in this invocation, this will be the entry that was just503 * inserted; in other words, if the map contains a single504 * entry, the eldest entry is also the newest.505 * @return true if the eldest entry should be removed506 * from the map; false if it should be retained.507 */508 protected boolean removeEldestEntry(Map.Entry

eldest) {509 return false;510 }511 512 /**513 * Returns a {
@link Set} view of the keys contained in this map.514 * The set is backed by the map, so changes to the map are515 * reflected in the set, and vice-versa. If the map is modified516 * while an iteration over the set is in progress (except through517 * the iterator's own
remove operation), the results of518 * the iteration are undefined. The set supports element removal,519 * which removes the corresponding mapping from the map, via the520 *
Iterator.remove,
Set.remove,521 *
removeAll,
retainAll, and
clear522 * operations. It does not support the
add or
addAll523 * operations.524 * Its {
@link Spliterator} typically provides faster sequential525 * performance but much poorer parallel performance than that of526 * {
@code HashMap}.527 *528 * @return a set view of the keys contained in this map529 */530 public Set
keySet() {531 Set
ks = keySet;532 if (ks == null) {533 ks = new LinkedKeySet();534 keySet = ks;535 }536 return ks;537 }538 539 final class LinkedKeySet extends AbstractSet
{540 public final int size() { return size; }541 public final void clear() { LinkedHashMap.this.clear(); }542 public final Iterator
iterator() {543 return new LinkedKeyIterator();544 }545 public final boolean contains(Object o) { return containsKey(o); }546 public final boolean remove(Object key) {547 return removeNode(hash(key), key, null, false, true) != null;548 }549 public final Spliterator
spliterator() {550 return Spliterators.spliterator(this, Spliterator.SIZED |551 Spliterator.ORDERED |552 Spliterator.DISTINCT);553 }554 public final void forEach(Consumer
action) {555 if (action == null)556 throw new NullPointerException();557 int mc = modCount;558 for (LinkedHashMap.Entry
e = head; e != null; e = e.after)559 action.accept(e.key);560 if (modCount != mc)561 throw new ConcurrentModificationException();562 }563 }564 565 /**566 * Returns a { @link Collection} view of the values contained in this map.567 * The collection is backed by the map, so changes to the map are568 * reflected in the collection, and vice-versa. If the map is569 * modified while an iteration over the collection is in progress570 * (except through the iterator's own
remove operation),571 * the results of the iteration are undefined. The collection572 * supports element removal, which removes the corresponding573 * mapping from the map, via the
Iterator.remove,574 *
Collection.remove,
removeAll,575 *
retainAll and
clear operations. It does not576 * support the
add or
addAll operations.577 * Its { @link Spliterator} typically provides faster sequential578 * performance but much poorer parallel performance than that of579 * { @code HashMap}.580 *581 * @return a view of the values contained in this map582 */583 public Collection
values() {584 Collection
vs = values;585 if (vs == null) {586 vs = new LinkedValues();587 values = vs;588 }589 return vs;590 }591 592 final class LinkedValues extends AbstractCollection
{593 public final int size() { return size; }594 public final void clear() { LinkedHashMap.this.clear(); }595 public final Iterator
iterator() {596 return new LinkedValueIterator();597 }598 public final boolean contains(Object o) { return containsValue(o); }599 public final Spliterator
spliterator() {600 return Spliterators.spliterator(this, Spliterator.SIZED |601 Spliterator.ORDERED);602 }603 public final void forEach(Consumer
action) {604 if (action == null)605 throw new NullPointerException();606 int mc = modCount;607 for (LinkedHashMap.Entry
e = head; e != null; e = e.after)608 action.accept(e.value);609 if (modCount != mc)610 throw new ConcurrentModificationException();611 }612 }613 614 /**615 * Returns a { @link Set} view of the mappings contained in this map.616 * The set is backed by the map, so changes to the map are617 * reflected in the set, and vice-versa. If the map is modified618 * while an iteration over the set is in progress (except through619 * the iterator's own
remove operation, or through the620 *
setValue operation on a map entry returned by the621 * iterator) the results of the iteration are undefined. The set622 * supports element removal, which removes the corresponding623 * mapping from the map, via the
Iterator.remove,624 *
Set.remove,
removeAll,
retainAll and625 *
clear operations. It does not support the626 *
add or
addAll operations.627 * Its { @link Spliterator} typically provides faster sequential628 * performance but much poorer parallel performance than that of629 * { @code HashMap}.630 *631 * @return a set view of the mappings contained in this map632 */633 public Set
> entrySet() {634 Set
> es;635 return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;636 }637 638 final class LinkedEntrySet extends AbstractSet
> {639 public final int size() { return size; }640 public final void clear() { LinkedHashMap.this.clear(); }641 public final Iterator
> iterator() {642 return new LinkedEntryIterator();643 }644 public final boolean contains(Object o) {645 if (!(o instanceof Map.Entry))646 return false;647 Map.Entry
e = (Map.Entry
) o;648 Object key = e.getKey();649 Node
candidate = getNode(hash(key), key);650 return candidate != null && candidate.equals(e);651 }652 public final boolean remove(Object o) {653 if (o instanceof Map.Entry) {654 Map.Entry
e = (Map.Entry
) o;655 Object key = e.getKey();656 Object value = e.getValue();657 return removeNode(hash(key), key, value, true, true) != null;658 }659 return false;660 }661 public final Spliterator
> spliterator() {662 return Spliterators.spliterator(this, Spliterator.SIZED |663 Spliterator.ORDERED |664 Spliterator.DISTINCT);665 }666 public final void forEach(Consumer
> action) {667 if (action == null)668 throw new NullPointerException();669 int mc = modCount;670 for (LinkedHashMap.Entry
e = head; e != null; e = e.after)671 action.accept(e);672 if (modCount != mc)673 throw new ConcurrentModificationException();674 }675 }676 677 // Map overrides678 679 public void forEach(BiConsumer
action) {680 if (action == null)681 throw new NullPointerException();682 int mc = modCount;683 for (LinkedHashMap.Entry
e = head; e != null; e = e.after)684 action.accept(e.key, e.value);685 if (modCount != mc)686 throw new ConcurrentModificationException();687 }688 689 public void replaceAll(BiFunction
function) {690 if (function == null)691 throw new NullPointerException();692 int mc = modCount;693 for (LinkedHashMap.Entry
e = head; e != null; e = e.after)694 e.value = function.apply(e.key, e.value);695 if (modCount != mc)696 throw new ConcurrentModificationException();697 }698 699 // Iterators700 701 abstract class LinkedHashIterator {702 LinkedHashMap.Entry
next;703 LinkedHashMap.Entry
current;704 int expectedModCount;705 706 LinkedHashIterator() {707 next = head;708 expectedModCount = modCount;709 current = null;710 }711 712 public final boolean hasNext() {713 return next != null;714 }715 716 final LinkedHashMap.Entry
nextNode() {717 LinkedHashMap.Entry
e = next;718 if (modCount != expectedModCount)719 throw new ConcurrentModificationException();720 if (e == null)721 throw new NoSuchElementException();722 current = e;723 next = e.after;724 return e;725 }726 727 public final void remove() {728 Node
p = current;729 if (p == null)730 throw new IllegalStateException();731 if (modCount != expectedModCount)732 throw new ConcurrentModificationException();733 current = null;734 K key = p.key;735 removeNode(hash(key), key, null, false, false);736 expectedModCount = modCount;737 }738 }739 740 final class LinkedKeyIterator extends LinkedHashIterator741 implements Iterator
{742 public final K next() { return nextNode().getKey(); }743 }744 745 final class LinkedValueIterator extends LinkedHashIterator746 implements Iterator
{747 public final V next() { return nextNode().value; }748 }749 750 final class LinkedEntryIterator extends LinkedHashIterator751 implements Iterator
> {752 public final Map.Entry
next() { return nextNode(); }753 }754 755 756 }

View Code

继续调试,找到DispatcherServlet的doDispatcher第72行并进入processDispatchResult方法:

1     /** 2      * Handle the result of handler selection and handler invocation, which is 3      * either a ModelAndView or an Exception to be resolved to a ModelAndView. 4      */ 5     private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 6             @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, 7             @Nullable Exception exception) throws Exception { 8  9         boolean errorView = false;10 11         if (exception != null) {12             if (exception instanceof ModelAndViewDefiningException) {13                 logger.debug("ModelAndViewDefiningException encountered", exception);14                 mv = ((ModelAndViewDefiningException) exception).getModelAndView();15             }16             else {17                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);18                 mv = processHandlerException(request, response, handler, exception);19                 errorView = (mv != null);20             }21         }22 23         // Did the handler return a view to render?24         if (mv != null && !mv.wasCleared()) {25             render(mv, request, response);26             if (errorView) {27                 WebUtils.clearErrorRequestAttributes(request);28             }29         }30         else {31             if (logger.isDebugEnabled()) {32                 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +33                         "': assuming HandlerAdapter completed request handling");34             }35         }36 37         if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {38             // Concurrent handling started during a forward39             return;40         }41 42         if (mappedHandler != null) {43             mappedHandler.triggerAfterCompletion(request, response, null);44         }45     }

继续调试,找到DispatcherServlet的processDispatchResult第25行并进入render方法:

1     /** 2      * Render the given ModelAndView. 3      * 

This is the last stage in handling a request. It may involve resolving the view by name. 4 * @param mv the ModelAndView to render 5 * @param request current HTTP servlet request 6 * @param response current HTTP servlet response 7 * @throws ServletException if view is missing or cannot be resolved 8 * @throws Exception if there's a problem rendering the view 9 */10 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {11 // Determine locale for request and apply it to the response.12 Locale locale =13 (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());14 response.setLocale(locale);15 16 View view;17 String viewName = mv.getViewName();18 if (viewName != null) {19 // We need to resolve the view name.20 view = resolveViewName(viewName, mv.getModelInternal(), locale, request);21 if (view == null) {22 throw new ServletException("Could not resolve view with name '" + mv.getViewName() +23 "' in servlet with name '" + getServletName() + "'");24 }25 }26 else {27 // No need to lookup: the ModelAndView object contains the actual View object.28 view = mv.getView();29 if (view == null) {30 throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +31 "View object in servlet with name '" + getServletName() + "'");32 }33 }34 35 // Delegate to the View object for rendering.36 if (logger.isDebugEnabled()) {37 logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");38 }39 try {40 if (mv.getStatus() != null) {41 response.setStatus(mv.getStatus().value());42 }43 view.render(mv.getModelInternal(), request, response);44 }45 catch (Exception ex) {46 if (logger.isDebugEnabled()) {47 logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +48 getServletName() + "'", ex);49 }50 throw ex;51 }52 }

继续调试,找到是View接口类的render接口方法,CTRL+T查找引用类结构:

进入AbstractView抽象类,找到render方法:

1     /** 2      * Prepares the view given the specified model, merging it with static 3      * attributes and a RequestContext attribute, if necessary. 4      * Delegates to renderMergedOutputModel for the actual rendering. 5      * @see #renderMergedOutputModel 6      */ 7     @Override 8     public void render(@Nullable Map
model, HttpServletRequest request, 9 HttpServletResponse response) throws Exception {10 11 if (logger.isTraceEnabled()) {12 logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +13 " and static attributes " + this.staticAttributes);14 }15 16 Map
mergedModel = createMergedOutputModel(model, request, response);17 prepareResponse(request, response);18 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);19 }

找到第18行方法renderMergedOutputModel方法,并进入该方法:

protected abstract void renderMergedOutputModel(            Map
model, HttpServletRequest request, HttpServletResponse response) throws Exception;

此方法是AbstractView的一个抽象方法,CTRL+T:

从上图中我们可以发现renderMergedOutputModel的实现类中包含了InternalResourceView,而我们的web.xml配置的springDispatcherServlet指定的类就是该类,因此直接查看InternalResourceView类即可。

进入InternalResourceView类renderMergedOutputModel方法:

1     @Override 2     protected void renderMergedOutputModel( 3             Map
model, HttpServletRequest request, HttpServletResponse response) throws Exception { 4 5 // Expose the model object as request attributes. 6 exposeModelAsRequestAttributes(model, request); 7 8 // Expose helpers as request attributes, if any. 9 exposeHelpers(request);10 11 // Determine the path for the request dispatcher.12 String dispatcherPath = prepareForRendering(request, response);13 14 // Obtain a RequestDispatcher for the target resource (typically a JSP).15 RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);16 if (rd == null) {17 throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +18 "]: Check that the corresponding file exists within your web application archive!");19 }20 21 // If already included or response already committed, perform include, else forward.22 if (useInclude(request, response)) {23 response.setContentType(getContentType());24 if (logger.isDebugEnabled()) {25 logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");26 }27 rd.include(request, response);28 }29 30 else {31 // Note: The forwarded resource is supposed to determine the content type itself.32 if (logger.isDebugEnabled()) {33 logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");34 }35 rd.forward(request, response);36 }37 }

找到第6行exposeModelAsRequestAttributes(model, request);方法并进入,此时进入方法归属类为AbstractView:

1     /** 2      * Expose the model objects in the given map as request attributes. 3      * Names will be taken from the model Map. 4      * This method is suitable for all resources reachable by {
@link javax.servlet.RequestDispatcher}. 5 * @param model Map of model objects to expose 6 * @param request current HTTP request 7 */ 8 protected void exposeModelAsRequestAttributes(Map
model, 9 HttpServletRequest request) throws Exception {10 11 model.forEach((modelName, modelValue) -> {12 if (modelValue != null) {13 request.setAttribute(modelName, modelValue);14 if (logger.isDebugEnabled()) {15 logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +16 "] to request in view with name '" + getBeanName() + "'");17 }18 }19 else {20 request.removeAttribute(modelName);21 if (logger.isDebugEnabled()) {22 logger.debug("Removed model object '" + modelName +23 "' from request in view with name '" + getBeanName() + "'");24 }25 }26 });27 }

从该方法中我们可以总结出一个结论:

不管SpringMVC的handler类方法返回值是ModelAndView、String,也不管SpringMVC的handler类方法的入参是Map、Model、MapModel等,这些参数信息都会被存放到SpringMVC的request请求域中,这也是为什么success.jsp中显示currentTime时,采用${requestScope.currentTime}的原因。

 

转载于:https://www.cnblogs.com/yy3b2007com/p/8202769.html

你可能感兴趣的文章
ZJOI2018游记Round1
查看>>
侧边栏广告和回到顶部
查看>>
https://blog.csdn.net/u012106306/article/details/80760744
查看>>
【转】从头到尾彻底理解KMP
查看>>
ios应用版本号设置规则
查看>>
海上孤独的帆
查看>>
error: more than one device and emulator 问题解决
查看>>
Java基础:容器
查看>>
YUV摘要格式
查看>>
【方法2】删除Map中Value反复的记录,而且仅仅保留Key最小的那条记录
查看>>
C# CheckedListBox控件的使用方法
查看>>
【HDOJ】2007平方和与立方和
查看>>
SAP Netweaver Gateway Add-on for Backend System Installation
查看>>
[转载] cassandra Unable to complete request: one or more nodes were unavailable
查看>>
Framework7 无限滚动
查看>>
IoT设备WiFi配网及现状
查看>>
第一次冲刺-站立会议06
查看>>
java之Random类
查看>>
23种设计模式 视频教程
查看>>
ST公司三轴加速度计LIS3DH应用
查看>>