zoukankan      html  css  js  c++  java
  • SpringMVC(九):SpringMVC 处理输出模型数据之ModelAndView

    Spring MVC提供了以下几种途径输出模型数据:

    1)ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据;

    2)Map及Model:处理方法入参为org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map时,处理方法返回时,Map中的数据会自动被添加到模型中;

    3)@SessionAttributes:将模型中的某个属性暂存到HttpSeession中,以便多个请求之间可以共享这个属性;

    4)@ModelAttribute:方法入参标注该注解后,入参的对象就会放到数据模型中。

    ModelAndView

    用法示例:

    添加TestModelAndView.java handler类:

    package com.dx.springlearn.hanlders;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    
    @Controller
    public class TestModelData {
        private final String SUCCESS = "success";
    
        @RequestMapping("/testModelAndView")
        public ModelAndView testModelAndView() {
            String viewName = SUCCESS;
            ModelAndView modelAndView = new ModelAndView(viewName);
            modelAndView.addObject("currentTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    
            return modelAndView;
        }
    }

    修改index.jsp,添加链接:

        <a href="testModelAndView">test ModelAndView</a>
        <br />

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

        current time:${requestScope.currentTime}
        <br>

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

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

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

    DispatcherServlet的doDispatcher方法源代码为:

     1     /**
     2      * Process the actual dispatching to the handler.
     3      * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
     4      * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
     5      * to find the first that supports the handler class.
     6      * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
     7      * themselves to decide which methods are acceptable.
     8      * @param request current HTTP request
     9      * @param response current HTTP response
    10      * @throws Exception in case of any kind of processing failure
    11      */
    12     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    13         HttpServletRequest processedRequest = request;
    14         HandlerExecutionChain mappedHandler = null;
    15         boolean multipartRequestParsed = false;
    16 
    17         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    18 
    19         try {
    20             ModelAndView mv = null;
    21             Exception dispatchException = null;
    22 
    23             try {
    24                 processedRequest = checkMultipart(request);
    25                 multipartRequestParsed = (processedRequest != request);
    26 
    27                 // Determine handler for the current request.
    28                 mappedHandler = getHandler(processedRequest);
    29                 if (mappedHandler == null) {
    30                     noHandlerFound(processedRequest, response);
    31                     return;
    32                 }
    33 
    34                 // Determine handler adapter for the current request.
    35                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    36 
    37                 // Process last-modified header, if supported by the handler.
    38                 String method = request.getMethod();
    39                 boolean isGet = "GET".equals(method);
    40                 if (isGet || "HEAD".equals(method)) {
    41                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    42                     if (logger.isDebugEnabled()) {
    43                         logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
    44                     }
    45                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    46                         return;
    47                     }
    48                 }
    49 
    50                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    51                     return;
    52                 }
    53 
    54                 // Actually invoke the handler.
    55                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    56 
    57                 if (asyncManager.isConcurrentHandlingStarted()) {
    58                     return;
    59                 }
    60 
    61                 applyDefaultViewName(processedRequest, mv);
    62                 mappedHandler.applyPostHandle(processedRequest, response, mv);
    63             }
    64             catch (Exception ex) {
    65                 dispatchException = ex;
    66             }
    67             catch (Throwable err) {
    68                 // As of 4.3, we're processing Errors thrown from handler methods as well,
    69                 // making them available for @ExceptionHandler methods and other scenarios.
    70                 dispatchException = new NestedServletException("Handler dispatch failed", err);
    71             }
    72             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    73         }
    74         catch (Exception ex) {
    75             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    76         }
    77         catch (Throwable err) {
    78             triggerAfterCompletion(processedRequest, response, mappedHandler,
    79                     new NestedServletException("Handler processing failed", err));
    80         }
    81         finally {
    82             if (asyncManager.isConcurrentHandlingStarted()) {
    83                 // Instead of postHandle and afterCompletion
    84                 if (mappedHandler != null) {
    85                     mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    86                 }
    87             }
    88             else {
    89                 // Clean up any resources used by a multipart request.
    90                 if (multipartRequestParsed) {
    91                     cleanupMultipart(processedRequest);
    92                 }
    93             }
    94         }
    95     }

    结合第20行和第55行,我们可以得知:不管SpringMVC的handler类方法返回值是ModelAndView、String,也不管SpringMVC的handler类方法的入参是Map、Model、MapModel等,在SpringMVC内部都会把请求返回结果封装为一个ModelAndView。

    ModelAndView实际上内部存储结构就是一个Map<String,Object>,具体请查看ModelAndView源代码

      1 /*
      2  * Copyright 2002-2017 the original author or authors.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.springframework.web.servlet;
     18 
     19 import java.util.Map;
     20 
     21 import org.springframework.http.HttpStatus;
     22 import org.springframework.lang.Nullable;
     23 import org.springframework.ui.ModelMap;
     24 import org.springframework.util.CollectionUtils;
     25 
     26 /**
     27  * Holder for both Model and View in the web MVC framework.
     28  * Note that these are entirely distinct. This class merely holds
     29  * both to make it possible for a controller to return both model
     30  * and view in a single return value.
     31  *
     32  * <p>Represents a model and view returned by a handler, to be resolved
     33  * by a DispatcherServlet. The view can take the form of a String
     34  * view name which will need to be resolved by a ViewResolver object;
     35  * alternatively a View object can be specified directly. The model
     36  * is a Map, allowing the use of multiple objects keyed by name.
     37  *
     38  * @author Rod Johnson
     39  * @author Juergen Hoeller
     40  * @author Rob Harrop
     41  * @author Rossen Stoyanchev
     42  * @see DispatcherServlet
     43  * @see ViewResolver
     44  * @see HandlerAdapter#handle
     45  * @see org.springframework.web.servlet.mvc.Controller#handleRequest
     46  */
     47 public class ModelAndView {
     48 
     49     /** View instance or view name String */
     50     @Nullable
     51     private Object view;
     52 
     53     /** Model Map */
     54     @Nullable
     55     private ModelMap model;
     56 
     57     /** Optional HTTP status for the response */
     58     @Nullable
     59     private HttpStatus status;
     60 
     61     /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
     62     private boolean cleared = false;
     63 
     64 
     65     /**
     66      * Default constructor for bean-style usage: populating bean
     67      * properties instead of passing in constructor arguments.
     68      * @see #setView(View)
     69      * @see #setViewName(String)
     70      */
     71     public ModelAndView() {
     72     }
     73 
     74     /**
     75      * Convenient constructor when there is no model data to expose.
     76      * Can also be used in conjunction with {@code addObject}.
     77      * @param viewName name of the View to render, to be resolved
     78      * by the DispatcherServlet's ViewResolver
     79      * @see #addObject
     80      */
     81     public ModelAndView(String viewName) {
     82         this.view = viewName;
     83     }
     84 
     85     /**
     86      * Convenient constructor when there is no model data to expose.
     87      * Can also be used in conjunction with {@code addObject}.
     88      * @param view View object to render
     89      * @see #addObject
     90      */
     91     public ModelAndView(View view) {
     92         this.view = view;
     93     }
     94 
     95     /**
     96      * Create a new ModelAndView given a view name and a model.
     97      * @param viewName name of the View to render, to be resolved
     98      * by the DispatcherServlet's ViewResolver
     99      * @param model Map of model names (Strings) to model objects
    100      * (Objects). Model entries may not be {@code null}, but the
    101      * model Map may be {@code null} if there is no model data.
    102      */
    103     public ModelAndView(String viewName, @Nullable Map<String, ?> model) {
    104         this.view = viewName;
    105         if (model != null) {
    106             getModelMap().addAllAttributes(model);
    107         }
    108     }
    109 
    110     /**
    111      * Create a new ModelAndView given a View object and a model.
    112      * <emphasis>Note: the supplied model data is copied into the internal
    113      * storage of this class. You should not consider to modify the supplied
    114      * Map after supplying it to this class</emphasis>
    115      * @param view View object to render
    116      * @param model Map of model names (Strings) to model objects
    117      * (Objects). Model entries may not be {@code null}, but the
    118      * model Map may be {@code null} if there is no model data.
    119      */
    120     public ModelAndView(View view, @Nullable Map<String, ?> model) {
    121         this.view = view;
    122         if (model != null) {
    123             getModelMap().addAllAttributes(model);
    124         }
    125     }
    126 
    127     /**
    128      * Create a new ModelAndView given a view name and HTTP status.
    129      * @param viewName name of the View to render, to be resolved
    130      * by the DispatcherServlet's ViewResolver
    131      * @param status an HTTP status code to use for the response
    132      * (to be set just prior to View rendering)
    133      * @since 4.3.8
    134      */
    135     public ModelAndView(String viewName, HttpStatus status) {
    136         this.view = viewName;
    137         this.status = status;
    138     }
    139 
    140     /**
    141      * Create a new ModelAndView given a view name, model, and HTTP status.
    142      * @param viewName name of the View to render, to be resolved
    143      * by the DispatcherServlet's ViewResolver
    144      * @param model Map of model names (Strings) to model objects
    145      * (Objects). Model entries may not be {@code null}, but the
    146      * model Map may be {@code null} if there is no model data.
    147      * @param status an HTTP status code to use for the response
    148      * (to be set just prior to View rendering)
    149      * @since 4.3
    150      */
    151     public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) {
    152         this.view = viewName;
    153         if (model != null) {
    154             getModelMap().addAllAttributes(model);
    155         }
    156         this.status = status;
    157     }
    158 
    159     /**
    160      * Convenient constructor to take a single model object.
    161      * @param viewName name of the View to render, to be resolved
    162      * by the DispatcherServlet's ViewResolver
    163      * @param modelName name of the single entry in the model
    164      * @param modelObject the single model object
    165      */
    166     public ModelAndView(String viewName, String modelName, Object modelObject) {
    167         this.view = viewName;
    168         addObject(modelName, modelObject);
    169     }
    170 
    171     /**
    172      * Convenient constructor to take a single model object.
    173      * @param view View object to render
    174      * @param modelName name of the single entry in the model
    175      * @param modelObject the single model object
    176      */
    177     public ModelAndView(View view, String modelName, Object modelObject) {
    178         this.view = view;
    179         addObject(modelName, modelObject);
    180     }
    181 
    182 
    183     /**
    184      * Set a view name for this ModelAndView, to be resolved by the
    185      * DispatcherServlet via a ViewResolver. Will override any
    186      * pre-existing view name or View.
    187      */
    188     public void setViewName(@Nullable String viewName) {
    189         this.view = viewName;
    190     }
    191 
    192     /**
    193      * Return the view name to be resolved by the DispatcherServlet
    194      * via a ViewResolver, or {@code null} if we are using a View object.
    195      */
    196     @Nullable
    197     public String getViewName() {
    198         return (this.view instanceof String ? (String) this.view : null);
    199     }
    200 
    201     /**
    202      * Set a View object for this ModelAndView. Will override any
    203      * pre-existing view name or View.
    204      */
    205     public void setView(@Nullable View view) {
    206         this.view = view;
    207     }
    208 
    209     /**
    210      * Return the View object, or {@code null} if we are using a view name
    211      * to be resolved by the DispatcherServlet via a ViewResolver.
    212      */
    213     @Nullable
    214     public View getView() {
    215         return (this.view instanceof View ? (View) this.view : null);
    216     }
    217 
    218     /**
    219      * Indicate whether or not this {@code ModelAndView} has a view, either
    220      * as a view name or as a direct {@link View} instance.
    221      */
    222     public boolean hasView() {
    223         return (this.view != null);
    224     }
    225 
    226     /**
    227      * Return whether we use a view reference, i.e. {@code true}
    228      * if the view has been specified via a name to be resolved by the
    229      * DispatcherServlet via a ViewResolver.
    230      */
    231     public boolean isReference() {
    232         return (this.view instanceof String);
    233     }
    234 
    235     /**
    236      * Return the model map. May return {@code null}.
    237      * Called by DispatcherServlet for evaluation of the model.
    238      */
    239     @Nullable
    240     protected Map<String, Object> getModelInternal() {
    241         return this.model;
    242     }
    243 
    244     /**
    245      * Return the underlying {@code ModelMap} instance (never {@code null}).
    246      */
    247     public ModelMap getModelMap() {
    248         if (this.model == null) {
    249             this.model = new ModelMap();
    250         }
    251         return this.model;
    252     }
    253 
    254     /**
    255      * Return the model map. Never returns {@code null}.
    256      * To be called by application code for modifying the model.
    257      */
    258     public Map<String, Object> getModel() {
    259         return getModelMap();
    260     }
    261 
    262     /**
    263      * Set the HTTP status to use for the response.
    264      * <p>The response status is set just prior to View rendering.
    265      * @since 4.3
    266      */
    267     public void setStatus(@Nullable HttpStatus status) {
    268         this.status = status;
    269     }
    270 
    271     /**
    272      * Return the configured HTTP status for the response, if any.
    273      * @since 4.3
    274      */
    275     @Nullable
    276     public HttpStatus getStatus() {
    277         return this.status;
    278     }
    279 
    280 
    281     /**
    282      * Add an attribute to the model.
    283      * @param attributeName name of the object to add to the model
    284      * @param attributeValue object to add to the model (never {@code null})
    285      * @see ModelMap#addAttribute(String, Object)
    286      * @see #getModelMap()
    287      */
    288     public ModelAndView addObject(String attributeName, Object attributeValue) {
    289         getModelMap().addAttribute(attributeName, attributeValue);
    290         return this;
    291     }
    292 
    293     /**
    294      * Add an attribute to the model using parameter name generation.
    295      * @param attributeValue the object to add to the model (never {@code null})
    296      * @see ModelMap#addAttribute(Object)
    297      * @see #getModelMap()
    298      */
    299     public ModelAndView addObject(Object attributeValue) {
    300         getModelMap().addAttribute(attributeValue);
    301         return this;
    302     }
    303 
    304     /**
    305      * Add all attributes contained in the provided Map to the model.
    306      * @param modelMap a Map of attributeName -> attributeValue pairs
    307      * @see ModelMap#addAllAttributes(Map)
    308      * @see #getModelMap()
    309      */
    310     public ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap) {
    311         getModelMap().addAllAttributes(modelMap);
    312         return this;
    313     }
    314 
    315 
    316     /**
    317      * Clear the state of this ModelAndView object.
    318      * The object will be empty afterwards.
    319      * <p>Can be used to suppress rendering of a given ModelAndView object
    320      * in the {@code postHandle} method of a HandlerInterceptor.
    321      * @see #isEmpty()
    322      * @see HandlerInterceptor#postHandle
    323      */
    324     public void clear() {
    325         this.view = null;
    326         this.model = null;
    327         this.cleared = true;
    328     }
    329 
    330     /**
    331      * Return whether this ModelAndView object is empty,
    332      * i.e. whether it does not hold any view and does not contain a model.
    333      */
    334     public boolean isEmpty() {
    335         return (this.view == null && CollectionUtils.isEmpty(this.model));
    336     }
    337 
    338     /**
    339      * Return whether this ModelAndView object is empty as a result of a call to {@link #clear}
    340      * i.e. whether it does not hold any view and does not contain a model.
    341      * <p>Returns {@code false} if any additional state was added to the instance
    342      * <strong>after</strong> the call to {@link #clear}.
    343      * @see #clear()
    344      */
    345     public boolean wasCleared() {
    346         return (this.cleared && isEmpty());
    347     }
    348 
    349 
    350     /**
    351      * Return diagnostic information about this model and view.
    352      */
    353     @Override
    354     public String toString() {
    355         StringBuilder sb = new StringBuilder("ModelAndView: ");
    356         if (isReference()) {
    357             sb.append("reference to view with name '").append(this.view).append("'");
    358         }
    359         else {
    360             sb.append("materialized View is [").append(this.view).append(']');
    361         }
    362         sb.append("; model is ").append(this.model);
    363         return sb.toString();
    364     }
    365 
    366 }
    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  * <p>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<String, Object> {
     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      * <p><emphasis>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.</emphasis>
     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<String, ?> 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 getting
    125      * replaced).
    126      */
    127     public ModelMap mergeAttributes(@Nullable Map<String, ?> 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 attribute
    142      */
    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  * <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
     35  * with predictable iteration order.  This implementation differs from
     36  * <tt>HashMap</tt> 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  * (<i>insertion-order</i>).  Note that insertion order is not affected
     40  * if a key is <i>re-inserted</i> into the map.  (A key <tt>k</tt> is
     41  * reinserted into a map <tt>m</tt> if <tt>m.put(k, v)</tt> is invoked when
     42  * <tt>m.containsKey(k)</tt> would return <tt>true</tt> immediately prior to
     43  * the invocation.)
     44  *
     45  * <p>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  * <pre>
     51  *     void foo(Map m) {
     52  *         Map copy = new LinkedHashMap(m);
     53  *         ...
     54  *     }
     55  * </pre>
     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  * <p>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 (<i>access-order</i>).  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  * <i>No other methods generate entry accesses.</i>  In particular, operations
     74  * on collection-views do <i>not</i> affect the order of iteration of the
     75  * backing map.
     76  *
     77  * <p>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  * <p>This class provides all of the optional <tt>Map</tt> operations, and
     82  * permits null elements.  Like <tt>HashMap</tt>, it provides constant-time
     83  * performance for the basic operations (<tt>add</tt>, <tt>contains</tt> and
     84  * <tt>remove</tt>), assuming the hash function disperses elements
     85  * properly among the buckets.  Performance is likely to be just slightly
     86  * below that of <tt>HashMap</tt>, due to the added expense of maintaining the
     87  * linked list, with one exception: Iteration over the collection-views
     88  * of a <tt>LinkedHashMap</tt> requires time proportional to the <i>size</i>
     89  * of the map, regardless of its capacity.  Iteration over a <tt>HashMap</tt>
     90  * is likely to be more expensive, requiring time proportional to its
     91  * <i>capacity</i>.
     92  *
     93  * <p>A linked hash map has two parameters that affect its performance:
     94  * <i>initial capacity</i> and <i>load factor</i>.  They are defined precisely
     95  * as for <tt>HashMap</tt>.  Note, however, that the penalty for choosing an
     96  * excessively high value for initial capacity is less severe for this class
     97  * than for <tt>HashMap</tt>, as iteration times for this class are unaffected
     98  * by capacity.
     99  *
    100  * <p><strong>Note that this implementation is not synchronized.</strong>
    101  * If multiple threads access a linked hash map concurrently, and at least
    102  * one of the threads modifies the map structurally, it <em>must</em> be
    103  * synchronized externally.  This is typically accomplished by
    104  * synchronizing on some object that naturally encapsulates the map.
    105  *
    106  * If no such object exists, the map should be "wrapped" using the
    107  * {@link Collections#synchronizedMap Collections.synchronizedMap}
    108  * method.  This is best done at creation time, to prevent accidental
    109  * unsynchronized access to the map:<pre>
    110  *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));</pre>
    111  *
    112  * A structural modification is any operation that adds or deletes one or more
    113  * mappings or, in the case of access-ordered linked hash maps, affects
    114  * iteration order.  In insertion-ordered linked hash maps, merely changing
    115  * the value associated with a key that is already contained in the map is not
    116  * a structural modification.  <strong>In access-ordered linked hash maps,
    117  * merely querying the map with <tt>get</tt> is a structural modification.
    118  * </strong>)
    119  *
    120  * <p>The iterators returned by the <tt>iterator</tt> method of the collections
    121  * returned by all of this class's collection view methods are
    122  * <em>fail-fast</em>: if the map is structurally modified at any time after
    123  * the iterator is created, in any way except through the iterator's own
    124  * <tt>remove</tt> method, the iterator will throw a {@link
    125  * ConcurrentModificationException}.  Thus, in the face of concurrent
    126  * modification, the iterator fails quickly and cleanly, rather than risking
    127  * arbitrary, non-deterministic behavior at an undetermined time in the future.
    128  *
    129  * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
    130  * as it is, generally speaking, impossible to make any hard guarantees in the
    131  * presence of unsynchronized concurrent modification.  Fail-fast iterators
    132  * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
    133  * Therefore, it would be wrong to write a program that depended on this
    134  * exception for its correctness:   <i>the fail-fast behavior of iterators
    135  * should be used only to detect bugs.</i>
    136  *
    137  * <p>The spliterators returned by the spliterator method of the collections
    138  * returned by all of this class's collection view methods are
    139  * <em><a href="Spliterator.html#binding">late-binding</a></em>,
    140  * <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}.
    141  *
    142  * <p>This class is a member of the
    143  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
    144  * Java Collections Framework</a>.
    145  *
    146  * @implNote
    147  * The spliterators returned by the spliterator method of the collections
    148  * returned by all of this class's collection view methods are created from
    149  * the iterators of the corresponding collections.
    150  *
    151  * @param <K> the type of keys maintained by this map
    152  * @param <V> the type of mapped values
    153  *
    154  * @author  Josh Bloch
    155  * @see     Object#hashCode()
    156  * @see     Collection
    157  * @see     Map
    158  * @see     HashMap
    159  * @see     TreeMap
    160  * @see     Hashtable
    161  * @since   1.4
    162  */
    163 public class LinkedHashMap<K,V>
    164     extends HashMap<K,V>
    165     implements Map<K,V>
    166 {
    167 
    168     /*
    169      * Implementation note.  A previous version of this class was
    170      * internally structured a little differently. Because superclass
    171      * HashMap now uses trees for some of its nodes, class
    172      * LinkedHashMap.Entry is now treated as intermediary node class
    173      * that can also be converted to tree form. The name of this
    174      * class, LinkedHashMap.Entry, is confusing in several ways in its
    175      * current context, but cannot be changed.  Otherwise, even though
    176      * it is not exported outside this package, some existing source
    177      * code is known to have relied on a symbol resolution corner case
    178      * rule in calls to removeEldestEntry that suppressed compilation
    179      * errors due to ambiguous usages. So, we keep the name to
    180      * preserve unmodified compilability.
    181      *
    182      * The changes in node classes also require using two fields
    183      * (head, tail) rather than a pointer to a header node to maintain
    184      * the doubly-linked before/after list. This class also
    185      * previously used a different style of callback methods upon
    186      * access, insertion, and removal.
    187      */
    188 
    189     /**
    190      * HashMap.Node subclass for normal LinkedHashMap entries.
    191      */
    192     static class Entry<K,V> extends HashMap.Node<K,V> {
    193         Entry<K,V> before, after;
    194         Entry(int hash, K key, V value, Node<K,V> 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<K,V> head;
    205 
    206     /**
    207      * The tail (youngest) of the doubly linked list.
    208      */
    209     transient LinkedHashMap.Entry<K,V> tail;
    210 
    211     /**
    212      * The iteration ordering method for this linked hash map: <tt>true</tt>
    213      * for access-order, <tt>false</tt> for insertion-order.
    214      *
    215      * @serial
    216      */
    217     final boolean accessOrder;
    218 
    219     // internal utilities
    220 
    221     // link at the end of list
    222     private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    223         LinkedHashMap.Entry<K,V> 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 dst
    234     private void transferLinks(LinkedHashMap.Entry<K,V> src,
    235                                LinkedHashMap.Entry<K,V> dst) {
    236         LinkedHashMap.Entry<K,V> b = dst.before = src.before;
    237         LinkedHashMap.Entry<K,V> a = dst.after = src.after;
    238         if (b == null)
    239             head = dst;
    240         else
    241             b.after = dst;
    242         if (a == null)
    243             tail = dst;
    244         else
    245             a.before = dst;
    246     }
    247 
    248     // overrides of HashMap hook methods
    249 
    250     void reinitialize() {
    251         super.reinitialize();
    252         head = tail = null;
    253     }
    254 
    255     Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
    256         LinkedHashMap.Entry<K,V> p =
    257             new LinkedHashMap.Entry<K,V>(hash, key, value, e);
    258         linkNodeLast(p);
    259         return p;
    260     }
    261 
    262     Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
    263         LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
    264         LinkedHashMap.Entry<K,V> t =
    265             new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
    266         transferLinks(q, t);
    267         return t;
    268     }
    269 
    270     TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
    271         TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
    272         linkNodeLast(p);
    273         return p;
    274     }
    275 
    276     TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
    277         LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
    278         TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
    279         transferLinks(q, t);
    280         return t;
    281     }
    282 
    283     void afterNodeRemoval(Node<K,V> e) { // unlink
    284         LinkedHashMap.Entry<K,V> p =
    285             (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
    286         p.before = p.after = null;
    287         if (b == null)
    288             head = a;
    289         else
    290             b.after = a;
    291         if (a == null)
    292             tail = b;
    293         else
    294             a.before = b;
    295     }
    296 
    297     void afterNodeInsertion(boolean evict) { // possibly remove eldest
    298         LinkedHashMap.Entry<K,V> 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<K,V> e) { // move node to last
    306         LinkedHashMap.Entry<K,V> last;
    307         if (accessOrder && (last = tail) != e) {
    308             LinkedHashMap.Entry<K,V> p =
    309                 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
    310             p.after = null;
    311             if (b == null)
    312                 head = a;
    313             else
    314                 b.after = a;
    315             if (a != null)
    316                 a.before = b;
    317             else
    318                 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<K,V> 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 <tt>LinkedHashMap</tt> instance
    339      * with the specified initial capacity and load factor.
    340      *
    341      * @param  initialCapacity the initial capacity
    342      * @param  loadFactor      the load factor
    343      * @throws IllegalArgumentException if the initial capacity is negative
    344      *         or the load factor is nonpositive
    345      */
    346     public LinkedHashMap(int initialCapacity, float loadFactor) {
    347         super(initialCapacity, loadFactor);
    348         accessOrder = false;
    349     }
    350 
    351     /**
    352      * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
    353      * with the specified initial capacity and a default load factor (0.75).
    354      *
    355      * @param  initialCapacity the initial capacity
    356      * @throws IllegalArgumentException if the initial capacity is negative
    357      */
    358     public LinkedHashMap(int initialCapacity) {
    359         super(initialCapacity);
    360         accessOrder = false;
    361     }
    362 
    363     /**
    364      * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
    365      * 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 <tt>LinkedHashMap</tt> instance with
    374      * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>
    375      * instance is created with a default load factor (0.75) and an initial
    376      * capacity sufficient to hold the mappings in the specified map.
    377      *
    378      * @param  m the map whose mappings are to be placed in this map
    379      * @throws NullPointerException if the specified map is null
    380      */
    381     public LinkedHashMap(Map<? extends K, ? extends V> m) {
    382         super();
    383         accessOrder = false;
    384         putMapEntries(m, false);
    385     }
    386 
    387     /**
    388      * Constructs an empty <tt>LinkedHashMap</tt> instance with the
    389      * specified initial capacity, load factor and ordering mode.
    390      *
    391      * @param  initialCapacity the initial capacity
    392      * @param  loadFactor      the load factor
    393      * @param  accessOrder     the ordering mode - <tt>true</tt> for
    394      *         access-order, <tt>false</tt> for insertion-order
    395      * @throws IllegalArgumentException if the initial capacity is negative
    396      *         or the load factor is nonpositive
    397      */
    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 <tt>true</tt> if this map maps one or more keys to the
    408      * specified value.
    409      *
    410      * @param value value whose presence in this map is to be tested
    411      * @return <tt>true</tt> if this map maps one or more keys to the
    412      *         specified value
    413      */
    414     public boolean containsValue(Object value) {
    415         for (LinkedHashMap.Entry<K,V> 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      * <p>More formally, if this map contains a mapping from a key
    428      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
    429      * key.equals(k))}, then this method returns {@code v}; otherwise
    430      * it returns {@code null}.  (There can be at most one such mapping.)
    431      *
    432      * <p>A return value of {@code null} does not <i>necessarily</i>
    433      * indicate that the map contains no mapping for the key; it's also
    434      * possible that the map explicitly maps the key to {@code null}.
    435      * The {@link #containsKey containsKey} operation may be used to
    436      * distinguish these two cases.
    437      */
    438     public V get(Object key) {
    439         Node<K,V> 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<K,V> 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 <tt>true</tt> if this map should remove its eldest entry.
    469      * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
    470      * inserting a new entry into the map.  It provides the implementor
    471      * with the opportunity to remove the eldest entry each time a new one
    472      * is added.  This is useful if the map represents a cache: it allows
    473      * the map to reduce memory consumption by deleting stale entries.
    474      *
    475      * <p>Sample use: this override will allow the map to grow up to 100
    476      * entries and then delete the eldest entry each time a new entry is
    477      * added, maintaining a steady state of 100 entries.
    478      * <pre>
    479      *     private static final int MAX_ENTRIES = 100;
    480      *
    481      *     protected boolean removeEldestEntry(Map.Entry eldest) {
    482      *        return size() &gt; MAX_ENTRIES;
    483      *     }
    484      * </pre>
    485      *
    486      * <p>This method typically does not modify the map in any way,
    487      * instead allowing the map to modify itself as directed by its
    488      * return value.  It <i>is</i> permitted for this method to modify
    489      * the map directly, but if it does so, it <i>must</i> return
    490      * <tt>false</tt> (indicating that the map should not attempt any
    491      * further modification).  The effects of returning <tt>true</tt>
    492      * after modifying the map from within this method are unspecified.
    493      *
    494      * <p>This implementation merely returns <tt>false</tt> (so that this
    495      * 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 if
    498      *           this is an access-ordered map, the least recently accessed
    499      *           entry.  This is the entry that will be removed it this
    500      *           method returns <tt>true</tt>.  If the map was empty prior
    501      *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
    502      *           in this invocation, this will be the entry that was just
    503      *           inserted; in other words, if the map contains a single
    504      *           entry, the eldest entry is also the newest.
    505      * @return   <tt>true</tt> if the eldest entry should be removed
    506      *           from the map; <tt>false</tt> if it should be retained.
    507      */
    508     protected boolean removeEldestEntry(Map.Entry<K,V> 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 are
    515      * reflected in the set, and vice-versa.  If the map is modified
    516      * while an iteration over the set is in progress (except through
    517      * the iterator's own <tt>remove</tt> operation), the results of
    518      * the iteration are undefined.  The set supports element removal,
    519      * which removes the corresponding mapping from the map, via the
    520      * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
    521      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
    522      * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
    523      * operations.
    524      * Its {@link Spliterator} typically provides faster sequential
    525      * performance but much poorer parallel performance than that of
    526      * {@code HashMap}.
    527      *
    528      * @return a set view of the keys contained in this map
    529      */
    530     public Set<K> keySet() {
    531         Set<K> 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<K> {
    540         public final int size()                 { return size; }
    541         public final void clear()               { LinkedHashMap.this.clear(); }
    542         public final Iterator<K> 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<K> spliterator()  {
    550             return Spliterators.spliterator(this, Spliterator.SIZED |
    551                                             Spliterator.ORDERED |
    552                                             Spliterator.DISTINCT);
    553         }
    554         public final void forEach(Consumer<? super K> action) {
    555             if (action == null)
    556                 throw new NullPointerException();
    557             int mc = modCount;
    558             for (LinkedHashMap.Entry<K,V> 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 are
    568      * reflected in the collection, and vice-versa.  If the map is
    569      * modified while an iteration over the collection is in progress
    570      * (except through the iterator's own <tt>remove</tt> operation),
    571      * the results of the iteration are undefined.  The collection
    572      * supports element removal, which removes the corresponding
    573      * mapping from the map, via the <tt>Iterator.remove</tt>,
    574      * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
    575      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
    576      * support the <tt>add</tt> or <tt>addAll</tt> operations.
    577      * Its {@link Spliterator} typically provides faster sequential
    578      * performance but much poorer parallel performance than that of
    579      * {@code HashMap}.
    580      *
    581      * @return a view of the values contained in this map
    582      */
    583     public Collection<V> values() {
    584         Collection<V> 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<V> {
    593         public final int size()                 { return size; }
    594         public final void clear()               { LinkedHashMap.this.clear(); }
    595         public final Iterator<V> iterator() {
    596             return new LinkedValueIterator();
    597         }
    598         public final boolean contains(Object o) { return containsValue(o); }
    599         public final Spliterator<V> spliterator() {
    600             return Spliterators.spliterator(this, Spliterator.SIZED |
    601                                             Spliterator.ORDERED);
    602         }
    603         public final void forEach(Consumer<? super V> action) {
    604             if (action == null)
    605                 throw new NullPointerException();
    606             int mc = modCount;
    607             for (LinkedHashMap.Entry<K,V> 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 are
    617      * reflected in the set, and vice-versa.  If the map is modified
    618      * while an iteration over the set is in progress (except through
    619      * the iterator's own <tt>remove</tt> operation, or through the
    620      * <tt>setValue</tt> operation on a map entry returned by the
    621      * iterator) the results of the iteration are undefined.  The set
    622      * supports element removal, which removes the corresponding
    623      * mapping from the map, via the <tt>Iterator.remove</tt>,
    624      * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
    625      * <tt>clear</tt> operations.  It does not support the
    626      * <tt>add</tt> or <tt>addAll</tt> operations.
    627      * Its {@link Spliterator} typically provides faster sequential
    628      * performance but much poorer parallel performance than that of
    629      * {@code HashMap}.
    630      *
    631      * @return a set view of the mappings contained in this map
    632      */
    633     public Set<Map.Entry<K,V>> entrySet() {
    634         Set<Map.Entry<K,V>> es;
    635         return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
    636     }
    637 
    638     final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
    639         public final int size()                 { return size; }
    640         public final void clear()               { LinkedHashMap.this.clear(); }
    641         public final Iterator<Map.Entry<K,V>> 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<K,V> 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<Map.Entry<K,V>> spliterator() {
    662             return Spliterators.spliterator(this, Spliterator.SIZED |
    663                                             Spliterator.ORDERED |
    664                                             Spliterator.DISTINCT);
    665         }
    666         public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
    667             if (action == null)
    668                 throw new NullPointerException();
    669             int mc = modCount;
    670             for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
    671                 action.accept(e);
    672             if (modCount != mc)
    673                 throw new ConcurrentModificationException();
    674         }
    675     }
    676 
    677     // Map overrides
    678 
    679     public void forEach(BiConsumer<? super K, ? super V> action) {
    680         if (action == null)
    681             throw new NullPointerException();
    682         int mc = modCount;
    683         for (LinkedHashMap.Entry<K,V> 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<? super K, ? super V, ? extends V> function) {
    690         if (function == null)
    691             throw new NullPointerException();
    692         int mc = modCount;
    693         for (LinkedHashMap.Entry<K,V> 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     // Iterators
    700 
    701     abstract class LinkedHashIterator {
    702         LinkedHashMap.Entry<K,V> next;
    703         LinkedHashMap.Entry<K,V> 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<K,V> nextNode() {
    717             LinkedHashMap.Entry<K,V> 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<K,V> 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 LinkedHashIterator
    741         implements Iterator<K> {
    742         public final K next() { return nextNode().getKey(); }
    743     }
    744 
    745     final class LinkedValueIterator extends LinkedHashIterator
    746         implements Iterator<V> {
    747         public final V next() { return nextNode().value; }
    748     }
    749 
    750     final class LinkedEntryIterator extends LinkedHashIterator
    751         implements Iterator<Map.Entry<K,V>> {
    752         public final Map.Entry<K,V> 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 forward
    39             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      * <p>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<String, ?> 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<String, Object> mergedModel = createMergedOutputModel(model, request, response);
    17         prepareResponse(request, response);
    18         renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    19     }

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

    protected abstract void renderMergedOutputModel(
                Map<String, Object> 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<String, Object> 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<String, Object> 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}的原因。

     

  • 相关阅读:
    16-面向对象之语法(1)
    4-编辑器IDE_for_python
    3-python入门学习路线
    2-学习方法心得
    1-语法基础
    NSMutableArray基本概念
    NSArray 与字符串
    NSArray文件读写
    NSArray排序
    NSArray 遍历
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/8202769.html
Copyright © 2011-2022 走看看