手把手带你写一个 MiniSpring
郭屹
前 Sun Microsystems Java 研发工程师,开源软件 MiniSpring、MiniTomcat 开发者
6170 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 26 讲
手把手带你写一个 MiniSpring
15
15
1.0x
00:00/00:00
登录|注册

10|数据绑定: 如何自动转换传入的参数?

你好,我是郭屹。今天我们继续手写 MiniSpring,这节课我们讨论传入参数的转换问题。
上节课,我们已经基本完成了对 Dispatcher 的扩展,用 HanderMapping 来处理映射关系,用 HandlerAdapter 来处理映射后具体方法的调用。
在处理请求的过程中,我们用 ServletRequest 接收请求参数,而获取参数用的是 getParameter() 方法,它的返回值是 String 字符串,这也意味着无论是获取字符串参数、数字参数,还是布尔型参数,它获取到的返回值都是字符串。而如果要把请求参数转换成 Java 对象,就需要再处理,那么每一次获取参数后,都需要显式地编写大量重复代码,把 String 类型的参数转换成其他类型。显然这不符合我们对框架的期望,我们希望框架能帮助我们自动处理这些常规数据格式的转换。
再扩大到整个访问过程,后端处理完毕后,返回给前端的数据再做返回,也存在格式转换的问题,传入传出两个方向我们都要处理。而这节课我们讨论的重点是“传入”方向。

传入参数的绑定

我们先考虑传入方向的问题:请求参数怎么和 Java 对象里的属性进行自动映射?
这里,我们引入 WebDataBinder 来处理。这个类代表的是一个内部的目标对象,用于将 Request 请求内的字符串参数转换成不同类型的参数,来进行适配。所以比较自然的想法是这个类里面要持有一个目标对象 target,然后还要定义一个 bind() 方法,通过来绑定参数和目标对象,这是 WebDataBinder 里的核心。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了在处理请求参数时实现自动转换传入的参数的技术细节。作者首先指出了在处理请求过程中,使用ServletRequest接收请求参数,而获取参数用的是getParameter()方法,其返回值是String字符串,这意味着无论是获取字符串参数、数字参数,还是布尔型参数,都会得到字符串类型的返回值。为了将请求参数转换成Java对象,需要显式地编写大量重复代码,将String类型的参数转换成其他类型。文章重点讨论了“传入”方向的参数转换问题,并提出了解决方案,即希望框架能够自动处理常规数据格式的转换,避免开发者重复编写转换代码。 文章还介绍了EditorRegistrySupport的核心实现createDefaultEditors方法,内置了大量基本类型或包装类型的转换器Editor,以及WebDataBinder的完整实现和WebDataBinderFactory的使用方法。通过实现WebBindingInitializer接口和自定义的CustomEditor,读者可以了解如何支持自定义的Editor,从而让框架具有良好的扩展性。最后,通过改造invokeHandlerMethod方法,实现了参数绑定的过程。整体来看,本文详细介绍了如何处理请求参数的自动转换,以及相关框架和工具的使用方法,对于需要实现自动参数转换的开发者具有一定的参考价值。 总的来说,本文深入剖析了在处理请求参数时实现自动转换传入的参数的技术细节,为读者提供了全面的技术指导和实现方法。通过本文的学习,读者可以更好地理解参数转换的重要性,以及如何通过框架和工具实现自动参数转换,为其在实际开发中提供了有力的技术支持。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 MiniSpring》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(11)

  • 最新
  • 精选
  • C.
    this.webBindingInitializer = (WebBindingInitializer) this.webApplicationContext.getBean("webBindingInitializer"); 这段代码错误的原因是上一章节的DispatcherServlet文件 protected void refresh() { // 初始化 controller initController(); initHandlerMappings(this.webApplicationContext); initHandlerAdapters(this.parentApplicationContext); } 这个地方传递的容器不正确导致的,因为这两个容器一个是配置文件applicationContext.xml解析bean容器,一个是包扫描controller的bean容器。而webBindingInitializer这个bean的定义在applicationContext.xml配置文件中,所以传入webApplicationContext这个容器对象是获取不到的。改为parentApplicationContext就可以正确执行下去。可能后面章节还会改,但是本章节的内容结束出现了这个问题。

    作者回复: 赞!这是我喜欢的教学相长。学手艺就要这么眼脑手结合在一起,自己动手,而不是简单听讲,光看光听是学不会的。你的这个问题,后面都有改。

    2023-04-04归属地:江苏
    3
    1
  • lmnsds
    WebDataBinder类决定了本文的效果: - controller函数只能是一个自定义对象类型,且只能有一个参数 - request中的参数不能名不能是参数对象中没有的成员名称,否则会报错

    作者回复: 对。

    2023-05-16归属地:北京
  • 风轻扬
    这一讲,学到了以下知识点: 1)、如何将请求参数封装到实体类的字段上 2)、用接口,不要用实现类,增加扩展性 Spring框架真是应了现在很流行的一句服务标语:把困难留给自己,把方便带给客户。 另外,有2个问题,请教老师: 1)、为什么要翻译成客户化转换器呢?翻译成自定义转换器是不是更容易理解一点? 2)、BeanWrapperImpl中的getValue方法中,是一个笔误吗?应该是readMethod.setAccessible吧?另外,正常情况下,对外提供的set、get方法都是public的,不需要setAccessible为true了吧?

    作者回复: 翻译问题各自有习惯,你的翻译挺好。其实术语用英文原文更好,我线下课所有术语名词都是英语。 setAccessible是笔误,copy后没改。是不是public都用一下是个习惯。

    2023-04-15归属地:北京
    2
  • 袁帅
    @RequestMapping("/test3") public String doTest3(Integer a) {} 参数是Integer methodParameter.getType().newInstance(); 这行会报错啊 java.lang.NoSuchMethodException: java.lang.Integer.<init>()

    作者回复: 简单类型是有问题的,你看一下复杂对象的测试。简单类型和多参数,都是线下班的扩展练习。

    2023-04-10归属地:北京
    2
  • 不是早晨,就是黄昏
    所以我们应该怎么测试呢,在浏览器中输入的地址中参数应该是什么,应该给以下测试程序,来梳理整个流程,否则很乱

    作者回复: 感谢建议,我们后面把测试写得明细一点。

    2023-04-09归属地:河南
  • 梦某人
    目前这个似乎并不能完成对基本类型的转换,反而似乎要处理的是复合类型的转换?当然也可能是我代码和理解存在问题。 目前逻辑上是: Adapter 中对参数进行处理,对于每个参数都有一个 WebDataBinder 进行处理,而这个类在做类型绑定的时候,主要是通过 BeanWrapperImpl 类来进行处理,此时,每个 WebDataBinder 和 BeanWrapperImpl 内的 clazz 指向的都是这个参数的类,基本类型在这里会是一个 String、Long 之类的。 在 BeanWrapperImpl 的 setPropertyValue 方法中,主要是借助于由请求转换而来的 PropertyValue 类,这个PropertyValue 主要有 name 和value 是请求中的请求名和参数,并调用了 BeanPropertyHandler 以 PropertyValue 的 name 值进行处理。 BeanPropertyHandler 首先 根据请求名找到这个请求参数的类里面对应名称的 Field,再根据 Field 获取对应的 clazz,然后使用 Editor 的 getValue 来进行类型转换, 使用 set 方法进行赋值,然后使用对应属性的 get 进行取值操作。 但是基本类型没有 setString 和 getString 之类的情况。。。。。所以他反而没办法处理相对基础的类型。 解决方案应该是考虑配置默认类型的 writemethod 和 readmethod,,优化 BeanPropertyHandler 类。 现在给我的感觉就是 卡在了两头中间,类型转换可以完成,也能从请求中取到值,可以根据方法的参数列表构建对应的类型,但是中间基础类型的绑定的这一块是卡住无法处理的,就是基本类型是有问题的。 最后是思考题,如果要处理顺序问题,那么应该是在 apater中处理方法参数上的标记,根据注解或者标记来调整顺序。比如设置一个注解,指定同一类型的不同参数的名称,或者是指定顺序。然后在 adapter 中拿到方法后,根据注解重排这个方法的参数列表顺序。

    作者回复: 你的思考是对的,目前处理不了简单类型,也处理不了多参数。前几年的线下课这是作为学院扩展练习布置的。你考虑到了,想得很细,学思练,一定会收益很大。

    2023-04-07归属地:河北
  • 马儿
    感觉这节课的内容还是有点难懂.目前自己梳理了一下逻辑。大概有以下几个问题,希望老师能抽空解答一下 1.现在的代码似乎不能够解析基本类型,只能解析复杂类型。按照代码逻辑应该是将所有属性绑定到一个复杂类型中去,如果方法参数是基本类型就会报错NoSuchField 2.如果是复杂类型controller中每个参数都通过createbinder创建了WebDataBinder,但是WebDataBinder#BeanWrapperImpl每次都重新创建了BeanWrapperImpl对象,导致初始注册的CustomEditor在后续注册的时候并没有生效。

    作者回复: 用心了,高质量问题,你的理解都是对的。对基本类型的支持和多参数支持,这是扩展练习。MiniSpring是供学习用的,当时的线下班需要学员过程中补充更多特性。这就是我强调的眼脑手结合。我一直认为,编程是手工活儿,有点像学木匠,站在一边看师傅打家具是不能真掌握的这门手艺的,要自己动手。赞你。

    2023-04-04归属地:四川
  • 门窗小二
    增加一个类似requestParam的注解

    作者回复: 对。不过要进一步想想在程序哪个地方修改才能支持这个注解。

    2023-04-03归属地:福建
  • lmnsds
    http://localhost:8080/test4?name=yourname&id=2&birthday=2023-05-16 可以使用如上url进行测试 “/test4”对应的controller method 要以User为参数。User是定义在test.entity下的类。
    2023-05-16归属地:北京
    1
  • 云从
    自定义CustomEditor时,记得在RequestMappingHandlerAdapter的invokeHandlerMethod() 方法里面初始化一下 //注册binder中的editor-- 自定义editer webBindingInitializer.initBinder(wdb);
    2023-06-14归属地:四川
收起评论
显示
设置
留言
11
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部