springMVC参数绑定源码分析

news2025/7/19 16:53:27

一、遇到的问题

1. 在请求时get方法路径传参,接收时,用枚举接收,出现参数绑定错误

请求路径:http://localhost:9104/api/sent/test2?type=0

后端代码:

@GetMapping("/test2")
    public String openNewFile2(FileDTO param) {

        System.out.println("=====" + param);
        return "222";
    }
@Data
public class FileDTO {

    private SortTypeEnum type;
}

 

枚举类:

@Getter
@AllArgsConstructor
public enum SortTypeEnum {
    /**
     * 生序
     */
    ASC(0,"生序"),

    /**
     * 降序
     */
    DESC(1,"降序"),
    ;

    @JsonValue
    private final Integer code;
    private final String Label;
}

 

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult

org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object 'fileDTO' on field 'type': rejected value [0]; codes [typeMismatch.fileDTO.type,typeMismatch.type,typeMismatch.com.example.demoes.enums.SortTypeEnum,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [fileDTO.type,type]; arguments []; default message [type]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.example.demoes.enums.SortTypeEnum' for property 'type'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [com.example.demoes.enums.SortTypeEnum] for value '0'; nested exception is java.lang.IllegalArgumentException: No enum constant com.example.demoes.enums.SortTypeEnum.0]\r\n\tat org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:626)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n

经过排查发现,在StringToEnumConverterFactory---convert

public T convert(String source) {
    return source.isEmpty() ? null : Enum.valueOf(this.enumType, source.trim());
}

这个代码的时候,出现错误,上边代码是通过枚举的name去转换成枚举,实现参数绑定。

如果页面传枚举下标就会出错。

正确请求方式:

http://localhost:9104/api/sent/test2?type=ASC

这样就能绑定到参数上了。

2. 如果是post请求,就可以需要body传参

请求路径:

http://localhost:9104/api/sent/test4

body参数:

{
    "type":1
}

后端代码:

@PostMapping("/test4")
    public String openNewFile5(@RequestBody FileDTO param) {

        System.out.println("=====" + param);
        return "222";
    }

二、源码分析:

1. get参数绑定

在项目启动时会初始化默认的参数转换类:org.springframework.boot.autoconfigure.BackgroundPreinitializer.ConversionServiceInitializer

private static class ConversionServiceInitializer implements Runnable {
        private ConversionServiceInitializer() {
        }
// 会执行这个方法,初始化
        public void run() {
            new DefaultFormattingConversionService();
        }
    }
public DefaultFormattingConversionService(@Nullable StringValueResolver embeddedValueResolver, boolean registerDefaultFormatters) {
        if (embeddedValueResolver != null) {
            this.setEmbeddedValueResolver(embeddedValueResolver);
        }
        // 开始注册默认的
        DefaultConversionService.addDefaultConverters(this);
//这个方法也会执行
        if (registerDefaultFormatters) {
            addDefaultFormatters(this);
        }

    }

继续走到下面org.springframework.core.convert.support.DefaultConversionService#addDefaultConverters

这个方法会注册很多数据类型转换类

 public static void addDefaultConverters(ConverterRegistry converterRegistry) {
        addScalarConverters(converterRegistry);
        addCollectionConverters(converterRegistry);
        converterRegistry.addConverter(new ByteBufferConverter((ConversionService)converterRegistry));
        converterRegistry.addConverter(new StringToTimeZoneConverter());
        converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
        converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
        converterRegistry.addConverter(new ObjectToObjectConverter());
        converterRegistry.addConverter(new IdToEntityConverter((ConversionService)converterRegistry));
        converterRegistry.addConverter(new FallbackObjectToStringConverter());
        converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService)converterRegistry));
    }

在spring容器启动过程中,有个后置处理器

org.springframework.boot.SpringApplication#postProcessApplicationContext

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);
        }

        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);
            }

            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
//  这里会获取上边实现*Factory的转换类,放入conversionService字段中
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }

    }

这里会有166个转换类,被加载好放进去 

下面进行请求访问

第一次 请求会初始化servlet

org.springframework.web.servlet.DispatcherServlet#initStrategies---

注册本地的转换

initLocaleResolver(context);

 会创建bean--org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

将上边启动时创建好的ConversionService放入下边的字段中

org.springframework.beans.PropertyEditorRegistrySupport#setConversionService

由请求转发类到参数解析

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        } else {
// 走这里参数解析
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }
    }

绑定参数

this.bindRequestParameters(binder, webRequest);
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
		ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
		Assert.state(servletRequest != null, "No ServletRequest");
		ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
// 下面参数绑定,先从请求中获取参数
		servletBinder.bind(servletRequest);
	}

org.springframework.web.bind.ServletRequestDataBinder#bind

public void bind(ServletRequest request) {
// 请求获取参数
        MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
        MultipartRequest multipartRequest = (MultipartRequest)WebUtils.getNativeRequest(request, MultipartRequest.class);
        if (multipartRequest != null) {
            this.bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
        }

        this.addBindValues(mpvs, request);
        this.doBind(mpvs);
    }

 从请求获取参数的方法,都是用String接收的

org.springframework.web.util.WebUtils#getParametersStartingWith

 

会走到:org.springframework.validation.DataBinder#applyPropertyValues

参数绑定解析结果,找到要绑定的属性

 设置属性

org.springframework.beans.AbstractPropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues, boolean, boolean)

参数转换

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于优先级的时间片轮转调度算法(C语言实现)

已剪辑自: http://www.demodashi.com/demo/15341.html 基于优先级的时间片轮转调度算法 1. PCB结构(Block) 由此定义如下结构体: typedef struct Block {int processID; // 进程号int priority; // 优先级int status; // 状态double arriv…

PyQt5 JavaScript调用PyQt代码

JavaScript调用PyQt代码JavaScript调用PyQt代码,是指PyQt可以与加载的Web页面进行双向的数据交互。1.创建QWebChannel对象:创建QWebChannel对象,注册一个需要桥接的对象,以便Web页面的JavaScript使用。其核心代码如下:channel QW…

JUC并发编程与源码分析笔记01-本课程前置知识及要求说明

JUC是什么 JUC是指java.util.concurrent包,在并发编程中广泛使用。 官方文档搜索java.util.concurrent,可以看到有java.util.concurrent、java.util.concurrent。atomic、java.util.concurrent.locks。 本课程学生对象(非零基础&#xff09…

记 linux 系统编译好的exp提权提示无gcc

文章目录CVE-2021-4034 漏洞 polkit 提权在目标linux主机没有gcc的情况下提权,在很多情况下的一些内核漏洞需要在目标主机上使用gcc编译才可以正常运行,在本地编译好的exp如果本地系统与目标主机系统不一致,上传执行很大机会导致系统崩溃如脏…

糟了,线上服务出现OOM了

前言 前一段时间,公司同事的一个线上服务OOM的问题,我觉得挺有意思的,在这里跟大家一起分享一下。 我当时其实也参与了一部分问题的定位。 1 案发现场 他们有个mq消费者服务,在某一天下午,出现OOM了,导…

docker技术简介

目录 概念 命令 数据卷 DockerFile 应用部署 服务编排 私有仓库 概念 Docker 是一个开源的应用容器引擎,而容器技术是一种轻量级虚拟化方案(虚拟机太繁重了不够轻量级),Docker的基础是Linux容器(LXC&#xff09…

离线安装ceph集群(ceph-13.2.10)

记录:332 场景:在CentOS 7.9操作系统上,使用ceph的rpm-mimic的ceph-13.2.10安装ceph集群。应用ceph对象存储(ceph object store);应用ceph块设备(ceph block device);应用ceph文件系统(ceph file system)。 版本&…

数据结构(5)树形结构——二叉搜索树(JAVA代码实现)

5.1.概述 二叉搜索树,也叫二叉查找树、二叉排序树,顾名思义,这种二叉树是专门用来进行数据查找的二叉树。二叉搜索树的查找其实就是二分查找。 二叉搜索树的定义: 二叉搜索树可以为空如果二叉搜索树不为空,那么每个…

Design Compiler工具学习笔记(7)

目录 引言 背景知识 多时钟设计 DC 输出文件分析 实际操作 设计源码 综合脚本 综合网表 SDF文件 SDC文件 REPORT文件 引言 本篇继续学习 DC的基本使用。本篇主要学习 DC 综合之后的效果分析,多同步时钟设计以及 DC 综合完成之后的各种输出文件。 前文链…

微信小程序开发基础(03视图与逻辑)

学习目标 能够知道如何实现页面之间的导航跳转 能够知道如何实现下拉刷新效果 能够知道如何实现上拉加载更多效果 能够知道小程序中常用的生命周期函数 页面导航 1. 什么是页面导航 页面导航指的是页面之间的相互跳转。例如,浏览器中实现页面导航的方式有如下两…

关于环境保护html网页设计完整版,5个以环境为主题的网页设计与实现

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

《剑指 Offer 》—50. 第一个只出现一次的字符

《剑指 Offer 》—50. 第一个只出现一次的字符 一、题目内容 原题连接:https://leetcode.cn/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof/description/ 题目:在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。…

【专栏】核心篇06| Redis 存储高可用背后的模式

关注公众号 【离心计划】呀,一起逃离地球表面 Redis专栏合集 【专栏】01| Redis夜的第一章 【专栏】基础篇02| Redis 旁路缓存的价值 【专栏】基础篇03| Redis 花样的数据结构 【专栏】基础篇04| Redis 该怎么保证数据不丢失(上) 【专栏…

RabbitMQ------发布确认高级(消息回调、回退、备份交换机)(八)

RabbitMQ------发布确认高级(八) 可能由于某些意外情况,导致RabbitMQ重启,在RabbitMQ重启过程中,生产者投递消息失败,导致消息丢失。 如果才能够保证RabbitMQ的消息可靠性呢? 可能出现两种问题…

大数据毕设选题 - 深度学习火焰识别检测系统(python YOLO)

文章目录0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数:3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层5 数据集准备5.1 数据标…

CSRF漏洞简介

今天继续给大家介绍渗透测试相关知识,本文主要内容是CSRF漏洞原理、产生与危害。 免责声明: 本文所介绍的内容仅做学习交流使用,严禁利用文中技术进行非法行为,否则造成一切严重后果自负! 再次强调:严禁对未…

Maven——分模块开发与设计(重点)

目录 一、模块拆分 1.1 模块拆分思想 1.2 pojo模块拆分 1.3 dao模块拆分 1.4 service模块拆分 1.5 controller模块拆分 二、 聚合——模块聚合 三、继承——模块继承 一、模块拆分 1.1 模块拆分思想 左侧:我们从前的架构,一个人独立做的所有工作文件…

shell脚本的条件判断1:字符串和数字和比较

前言 写脚本时,为了让脚本更接近人类思考问题的方式,可以对各种情况进行判断。例如,经常需要判断某些条件是否成立,如果条件成立该如何处理,如果条件不成立又该如何处理,这些都可以通过Shell脚本的if语句结…

大数据_什么是数据中台?

目录 一、数据中台的定义 二、数据中台必备的是个核心能力 三、数据中台VS业务中台 四、数据中台VS数据仓库 五、数据中台VS现有信息架构 六、数据中台的业务价值与技术价值 一、数据中台的定义 数据中台是一套可持续“让企业的数据用起来”的机制,是一种战略…

vm的生命周期钩子

vm的生命周期钩子(共11个): 前8个: 将要创建>调用beforeCreate函数 创建完毕>调用created函数 将要挂载>调用beforeMount函数 (重要)挂载完毕>调用mounted函数>【重要钩子】 将要更新…