springboot源码解析之Model和Map参数解析

springboot源码解析之Model和Map参数解析

标签:源码:springboot

测试代码

@Controller
public class HelloController {

    @RequestMapping("/helloModelAndMap")
    public String helloModelAndMap(HttpServletRequest request, Model model, Map<String, Object> map) {
        System.out.println(model.getClass().getSimpleName());
        System.out.println(map.getClass().getSimpleName());
        System.out.println(model == map);
        request.setAttribute("message", "Hello World!");
        model.addAttribute("name", "张三");
        map.put("age", 18);
        // 这里拿不到Model和Map中的值,转发后能拿到 在视图渲染阶段才会放到请求域中
        System.out.println("helloMapAndModel:" + request.getAttribute("name"));
        System.out.println("helloMapAndModel:" + request.getAttribute("age"));
        return "forward:/success";
    }

    @RequestMapping("/success")
    @ResponseBody
    public Object success(ServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        result.put("message", request.getAttribute("message"));
        result.put("name", request.getAttribute("name"));
        result.put("age", request.getAttribute("age"));
        return result;
    }

}

控制台输出

BindingAwareModelMap
BindingAwareModelMap
true
helloMapAndModel:null
helloMapAndModel:null

请求响应

{"name":"张三","message":"Hello World!","age":18}

结果分析

通过输出可以看到,这里传入的Model对象和Map对象都是BindingAwareModelMap类型,并且指向同一个对象,BindingAwareModelMap既是Model也是Map

ModelAndViewContainer

image-20240306202335477

public class ModelAndViewContainer {

   // ...

   private final ModelMap defaultModel = new BindingAwareModelMap();

  
   public ModelMap getModel() {
      if (useDefaultModel()) {
         return this.defaultModel;
      }
      else {
         if (this.redirectModel == null) {
            this.redirectModel = new ModelMap();
         }
         return this.redirectModel;
      }
   }

   // ... 

}

MapMethodProcessor

Map是由MapMethodProcessor参数处理器处理的

public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

   @Override
   public boolean supportsParameter(MethodParameter parameter) {
       // 参数类型为Map,并且没有啥注解(所以如果想要用map接受前段传递的请求体里的参数,一定要不要忘记加上@RequestBody注解)
      return Map.class.isAssignableFrom(parameter.getParameterType()) &&
            parameter.getParameterAnnotations().length == 0;
   }

   @Override
   @Nullable
   public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

      Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
       // 解析参数 这里拿到了ModelAndViewContainer 里面的Model 也就是 BindingAwareModelMap
      return mavContainer.getModel();
   }

   @Override
   public boolean supportsReturnType(MethodParameter returnType) {
      return Map.class.isAssignableFrom(returnType.getParameterType());
   }

   @Override
   @SuppressWarnings({"rawtypes", "unchecked"})
   public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

      if (returnValue instanceof Map){
         mavContainer.addAllAttributes((Map) returnValue);
      }
      else if (returnValue != null) {
         // should not happen
         throw new UnsupportedOperationException("Unexpected return type: " +
               returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
      }
   }

}

ModelMethodProcessor

Model参数是由ModelMethodProcessor解析的

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
        // 参数类型是model
		return Model.class.isAssignableFrom(parameter.getParameterType());
	}

	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
        // 解析参数 和 MapMethodProcessor 一样,拿到了同一个 BindingAwareModelMap 对象,这也就是为什么测试方法里面model 和 map指向同一个对象的原因
		return mavContainer.getModel();
	}

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return Model.class.isAssignableFrom(returnType.getParameterType());
	}

	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		if (returnValue == null) {
			return;
		}
		else if (returnValue instanceof Model) {
			mavContainer.addAllAttributes(((Model) returnValue).asMap());
		}
		else {
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}

}

参数解析过程中只是拿到了 BindingAwareModelMap 对象,执行目标方法的时候只是将key和value设置到了 BindingAwareModelMap 对象里面,那么具体是在哪里将 BindingAwareModelMap 中的key和value设置到请求域中的呢?

AbstractView

org.springframework.web.servlet.view.AbstractView#exposeModelAsRequestAttributes

protected void exposeModelAsRequestAttributes(Map<String, Object> model,
                                              HttpServletRequest request) throws Exception {

    model.forEach((name, value) -> {
        if (value != null) {
            // 这样就是为什么给model 和 map中方数据和给request放数据一样的原因
            request.setAttribute(name, value);
        }
        else {
            request.removeAttribute(name);
        }
    });
}

通过调用链我们可以发现是在渲染视图的过程中将BindingAwareModelMap 对象中的值设置到请求域中的

image-20240306204213299

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/435862.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙文章专题-2021年鸿蒙相关的文章废弃

#原因 至于为什么说2021年我的鸿蒙专栏的文章废弃了&#xff0c;只是说没有了参考意义&#xff0c;是因为鸿蒙4.0以前的版本语言从以Java为主过渡为以ArkTS为主。以前的Java版本的工程已经无法再使用了&#xff0c;后续的开发都必须以ArkTS开发语言为主。 其中而且整个项目结构…

Vue深度教程

一、Vue简介 1.简介 2.快速上手 二、基础 1.创建一个Vue应用 2.模板语法 3.响应式基础 4.计算属性 5.Class与 Style绑定 6.条件渲染 7.列表渲染 8.事件处理 9.表单输入绑定 10.生命周期钩子 11.侦听器 12.模板引用 13.组件基础 三、深入组件 1.组件注册 2.Props 3.组件事件 …

vue ui Starting GUI 图形化配置web新项目

前言&#xff1a;在vue框架里面&#xff0c; 以往大家都是习惯用命令行 vue create 、vue init webpack创建新前端项目&#xff0c;而vue ui是一个可视化的图形界面&#xff0c;对于新手来说更加友好了&#xff0c;不但可以创建、管理、还可以更新vue项目&#xff0c;也可以下载…

实现swiper 3d 轮播效果

先上个效果图&#xff0c;代码可以直接拿~ 安装swiper和vue-awesome-swiper 因为项目用的是nuxt2&#xff0c;所以考虑到swiper的兼容问题&#xff0c;选择的是"swiper": “^5.2.0” 首先是安装swiper和vue-awesome-swiper&#xff0c;并指定版本 npm install s…

在 SpringBoot3 中使用 Mybatis-Plus 报错

在 SpringBoot3 中使用 Mybatis-Plus 报错 Property ‘sqlSessionFactory’ or ‘sqlSessionTemplate’ are required Caused by: java.lang.IllegalArgumentException: Property sqlSessionFactory or sqlSessionTemplate are requiredat org.springframework.util.Assert.no…

C语言如何设置随机数

本期介绍&#x1f356; 主要介绍&#xff1a;在C语言中如何创建一个随机数。 文章目录 1. rand函数2. srand函数3. time函数4. 设置随机数的范围 1. rand函数 想要生成随机数&#xff0c;就需要用到C语言提供的一个库函数叫rand&#xff0c;这个函数可以生成0~32767范围内的随机…

Ubuntu/Linux系统下Redis的基本操作命令

版本查询 redis-server --version # 或者redis-server -v 如上图所示&#xff0c;redis-server的版本为6.0.9,证明redis已经安装完成。 启动Redis服务 启动命令如下&#xff1a; redis-server启动成功如下所示&#xff1a; 启动过程中遇到如下问题时&#xff0c;杀死指定端…

可调恒定电流稳压器NSI50150ADT4G车规级LED驱动器 提供专业的汽车级照明解决方案

NSI50150ADT4G产品概述&#xff1a; NSI50150ADT4G可调恒定电流稳压器 (CCR) &#xff0c;是一款简单、经济和耐用的器件&#xff0c;适用于为 LED 中的调节电流提供成本高效的方案&#xff08;与恒定电流二极管 CCD 类似&#xff09;。该 (CCR) 基于自偏置晶体管 (SBT) 技术&…

波奇学Linux:信号的发送和保存

信号的发送的对象是pcb task_struct{ int signal; //0000 0000 .... 0001 进程pcb中存在int型的signal来保存信号&#xff0c;用位图的方式&#xff0c;比特位的0&#xff0c;1表示是否收到信号 比特位位置表示信号的编号。 发信号的本质就是修改task_struct的信号位图对应的…

Android 日志原理解析

一、Logcat 二、Dumpsys C:\Users\pengcheng.ding>adb shell dumpsys --help usage: dumpsysTo dump all services. or:dumpsys [-t TIMEOUT] [--priority LEVEL] [--clients] [--dump] [--pid] [--thread] [--help | -l | --skip SERVICES | SERVICE [ARGS]]--help: show…

学生护眼台灯哪种好?揭秘央视公认好用的护眼台灯

现在近视已经成为了普遍的现象&#xff0c;尤其是学生群体&#xff0c;究其原因主要还是长期是电子设备、长时间的用眼疲劳以及不合适的用光环境&#xff0c;很多时候我们不经意的错误习惯都会导致不知不觉中近视。尤其是桌面上的那一盏台灯&#xff0c;如果台灯的质量太差也是…

Java批量操作文件系统

一、实验内容 编写一个文件管理器&#xff0c;实现文件的批量操作。文件管理器具体功能要求如下&#xff1a; 1、用户输入指令1&#xff0c;代表“指定关键字检索文件”&#xff0c;此时需要用户输入检索的目录和关键字&#xff0c;系统在用户指定的目录下检索出文件名中包含…

DataGrip(IDEA 内置)连接 SQL Server

原文&#xff1a;https://blog.iyatt.com/?p14265 测试环境&#xff1a; IDEA 2023.1SQL Server 2022 首先打开 SQL Server 配置管理工具 启用 TCP/IP 打开 Windows 服务管理 在服务列表中找到 SQL Server&#xff08;MSSQLSERVER&#xff09;&#xff0c;右键重新启…

PostgreSQL 安装部署

文章目录 一、PostgreSQL部署方式1.Yum方式部署2.RPM方式部署3.源码方式部署4.二进制方式部署5.Docker方式部署 二、PostgreSQL部署1.Yum方式部署1.1.部署数据库1.2.连接数据库 2.RPM方式部署2.1.部署数据库2.2.连接数据库 3.源码方式部署3.1.准备工作3.2.编译安装3.3.配置数据…

Android视角看鸿蒙第一课(工程目录)

Android视角看鸿蒙第一课&#xff08;工程目录&#xff09; 导读 鸿蒙马上就来了&#xff0c;这个工作很有可能落到Android开发的头上&#xff0c;既是机遇也是挑战&#xff0c;希望能跟上时代的浪潮&#xff0c;迫不得已开始学习鸿蒙开发&#xff0c;顺带分享记录下 我的学…

新书速览|PyTorch语音识别实战(人工智能技术丛书)

实战语音唤醒、音频特征抽取、语音情绪分类、Whisper语音转换、鸟叫多标签分类、多模态语音文字转换 01 本书内容 《PyTorch语音识别实战》使用PyTorch 2.0作为语音识别的基本框架&#xff0c;循序渐进地引导读者从搭建环境开始&#xff0c;逐步深入到语音识别基本理论、算法以…

解决QMYSQL driver not loaded问题

前言 之前都是在Qt5.51上开发&#xff0c;连接mysql数据库一直没有问题&#xff0c;换到5.15.2后一直报错 一查才发现\5.15.2\msvc2019_64\plugins\sqldrivers目录下没有qsqlmysql了&#xff0c;5.5.1是有的&#xff0c;5.15.2是要自己编译的。。。 下载源码 安装qt的时候没…

阿里云游戏访问与下载加速解决方案

随着全球化的发展&#xff0c;越来越多的企业开始将业务扩展到海外市场。然而&#xff0c;海外市场的挑战也是显而易见的。例如在游戏企业进军海外市场的过程中&#xff0c;网络延迟是一个常见的问题。由于地理位置的限制&#xff0c;海外用户登录访问游戏和游玩时&#xff0c;…

阿里云服务器Ngnix配置SSL证书开启HTTPS访问

文章目录 前言一、SSL证书是什么&#xff1f;二、如何获取免费SSL证书三、Ngnix配置SSL证书总结 前言 很多童鞋的网站默认访问都是通过80端口的Http服务进行访问&#xff0c;往往都会提示不安全&#xff0c;很多人以为Https有多么高大上&#xff0c;实际不然&#xff0c;他只是…

虚拟机时间同步主机

1.查看是否设置同步 2.查看时区 date -R 0800 表示时区东八区 明显不对 执行指令&#xff1a; tzselect &#xff1b;找到亚洲-中国-北京 3.覆盖一下文件 复制文件到 /etc/localtime 目录下&#xff1a;#sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 4.重现查…