• SpringMVC源码解析


    HandlerAdapter在处理请求时上下文数据的传递工作是由ModelAndViewContainer负责的.

    源码注释是这样描述的:

    Records model and view related decisions made by HandlerMethodArgumentResolvers and HandlerMethodReturnValueHandlers during the course of invocation of a controller method.

    翻译下: 记录HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 在处理handler时 使用的模型model和视图view相关信息.

    ModelAndViewContainer主要职责:

      1. 维护模型model,包括defaultModle和redirectModel

      2. 维护视图view

      3. 维护是否redirect信息,及根据这个判断HandlerAdapter使用的是defaultModel或redirectModel(判断规则详见下文)

      4. 维护@SessionAttributes注解信息状态

      5. 维护handler是否处理标记

    目录:

    1. ModelAndViewContainer属性

    2. 科普ModelMap继承体系

    3. ModelAndView提供的api,复杂的还是model相关的属性设置,其他主要是简单的getter,setter

    ModelAndViewContainer属性

    先来看看ModelAndViewContainer的属性,这样就比较清晰:

     1 package org.springframework.web.method.support;
     2 public class ModelAndViewContainer {
     3     // 视图,实际使用时可能是String类型的逻辑视图
     4     private Object view;
     5     // 标记handler是否已经完成请求处理
     6     private boolean requestHandled = false;
     7     // 默认模型,下文我们可以简单科普下ModelMap继承体系
     8     private final ModelMap defaultModel = new BindingAwareModelMap();
     9     // redirect时使用的模型,实际使用的是RedirectAttributesModelMap
    10     private ModelMap redirectModel;
    11     // 标记处理器返回redirect视图
    12     private boolean redirectModelScenario = false;
    13     // redirect时,是否忽略defaultModel
    14     private boolean ignoreDefaultModelOnRedirect = false;
    15     // @SessionAttributes注解使用状态标记,就是是否处理完毕
    16     private final SessionStatus sessionStatus = new SimpleSessionStatus();
    17 }

     顺便学个英语单词,Scenario 方案

    科普ModelMap继承体系

    继续往下分析之前,我们先来简单科普下ModelMap的继承体系:

    各个类的职责与使用场景:

      ModelMap是LinkedHashMap的子类,主要是封装attribute概念,实际处理还是委托给map

      ExtendedModelMap添加链调用chained calls,并实现Model接口

      BindingAwareModelMap,BindingResult相关属性被设置时,自动清除BindingResult.defaultModel使用的该类.

      RedirectAttributesModelMap 通过DataBinder做参数类型转换,redirect时使用的flash attributes

    各有侧重地看下源码吧

      2.1 ModelMap,是继承LinkedHashMap,添加mergeAttributes 

     1 package org.springframework.ui;
     2 public class ModelMap extends LinkedHashMap<String, Object> {
     3     public ModelMap mergeAttributes(Map<String, ?> attributes) {
     4         if (attributes != null) {
     5             for (String key : attributes.keySet()) {
     6                 if (!containsKey(key)) {
     7                     put(key, attributes.get(key));
     8                 }
     9             }
    10         }
    11         return this;
    12     }
    13     // ...
    14 }

      2.2 ExtendedModelMap添加链调用chained calls

    1 package org.springframework.ui;
    2 public class ExtendedModelMap extends ModelMap implements Model {
    3     @Override
    4     public ExtendedModelMap addAttribute(String attributeName, Object attributeValue) {
    5         super.addAttribute(attributeName, attributeValue);
    6         return this;// 看这里
    7     }
    8     // ...
    9 }

      2.3 BindingAwareModelMap,BindingResult相关属性被设置时,自动清除BindingResult.defaultModel使用的该类.

      看的就是removeBindingResultIfNecessary.这边put和putAll都会调用removeBindingResultIfNecessary

     1 package org.springframework.validation.support;
     2 public class BindingAwareModelMap extends ExtendedModelMap {
     3 
     4     @Override
     5     public Object put(String key, Object value) {
     6         removeBindingResultIfNecessary(key, value);
     7         return super.put(key, value);
     8     }
     9     // ...
    10     private void removeBindingResultIfNecessary(Object key, Object value) {
    11         if (key instanceof String) {
    12             String attributeName = (String) key;
    13             if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
    14                 String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attributeName;
    15                 BindingResult bindingResult = (BindingResult) get(bindingResultKey);
    16                 if (bindingResult != null && bindingResult.getTarget() != value) {
    17                     remove(bindingResultKey);
    18                 }
    19             }
    20         }
    21     }
    22 
    23 }

      2.4 RedirectAttributesModelMap 通过DataBinder做参数类型转换,redirect时使用的flash attributes

      这样主要就是添加了dataBinder和flashAttributes属性相关的api

     1 package org.springframework.web.servlet.mvc.support;
     2 public class RedirectAttributesModelMap extends ModelMap implements RedirectAttributes {
     3 
     4     private final DataBinder dataBinder;
     5 
     6     private final ModelMap flashAttributes = new ModelMap();
     7 
     8     private String formatValue(Object value) {
     9         if (value == null) {
    10             return null;
    11         }
    12         return (dataBinder != null) ? dataBinder.convertIfNecessary(value, String.class) : value.toString();
    13     }
    14     // ...
    15 }

    ModelAndView提供的api

    主要是两类api,view相关和model相关.

      3.1 view相关其实就一个api,是否类应用(通过是否是string类型判断):

     1 package org.springframework.web.method.support;
     2 public class ModelAndViewContainer {
     3     // ...
     4     /**
     5      * Whether the view is a view reference specified via a name to be
     6      * resolved by the DispatcherServlet via a ViewResolver.
     7      */
     8     public boolean isViewReference() {
     9         return (this.view instanceof String);
    10     }
    11 }

      3.2 model相关的api就比较多了,主要是设置模型值.

      在设置模型值的时候,这边涉及到一个问题,就是container里有两个model,往哪个里设置?

      container索性抽象一个getModel()的api进行判断.

      我们来说说判断的逻辑吧:

        使用defaultModel的场景:

        a,不是redirect  

        b,redirect场景下,redirectModel为null,同时ignoreDefaultModelOnRedirect为false

        剩下的就是使用redirectModel的场景了,不细说

      model相关属性操作的类都是跟addAttribute类似的逻辑,使用getModel获取model后直接委托.所以篇幅缘故下面的源码以addAttribute为例,其他都只是展现api 的signature

     1 package org.springframework.web.method.support;
     2 public class ModelAndViewContainer {
     3     // ...
     4     /**
     5      * Return the model to use: the "default" or the "redirect" model.
     6      * <p>The default model is used if {@code "redirectModelScenario=false"} or
     7      * if the redirect model is {@code null} (i.e. it wasn't declared as a
     8      * method argument) and {@code ignoreDefaultModelOnRedirect=false}.
     9      */
    10     public ModelMap getModel() {
    11         if (useDefaultModel()) {
    12             return this.defaultModel;
    13         }
    14         else {
    15             return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
    16         }
    17     }
    18     /**
    19      * Whether to use the default model or the redirect model.
    20      * 不是redirect || redirect时redirectModel为空同时不忽略defaultModel
    21      */
    22     private boolean useDefaultModel() {
    23         return !this.redirectModelScenario || ((this.redirectModel == null) && !this.ignoreDefaultModelOnRedirect);
    24     }
    25     public ModelAndViewContainer addAttribute(String name, Object value) {
    26         getModel().addAttribute(name, value);
    27         return this;
    28     }
    29     public ModelAndViewContainer addAttribute(Object value){};
    30     public ModelAndViewContainer addAllAttributes(Map<String, ?> attributes){};
    31     public ModelAndViewContainer removeAttributes(Map<String, ?> attributes){};
    32     public boolean containsAttribute(String name){};
    33 }
  • 相关阅读:
    redis-原理-数据结构-链表(二)
    redis-原理-数据结构-SDS(一)
    springMVC源码阅读-解决body不能重复读取问题(十二)
    spring-security使用-安全防护HttpFirewall(七)
    Redis-6.2.1 主从和哨兵模式配置
    Navicat 连接Mysql 8.0以上版本报错1251
    docker logs命令查看容器的日志
    Nginx 反向代理
    docker swarm 删除节点 (解散集群)
    Docker Swarm 命令学习
  • 原文地址:https://www.cnblogs.com/leftthen/p/5222753.html
Copyright © 2020-2023  润新知