SpringMVC请求方法的数据绑定

SpringMVC请求方法的数据绑定

我们在使用SpringMVC时,HTTP请求的信息能够自动绑定到相应方法的参数上,必要时可以使用@PathVariable,@RequestParam,@RequestHeader等注解。本文将探讨SpringMVC是如何实现方法参数的数据绑定,以及自定义数据的绑定实现。

数据绑定流程

Spring MVC通过反射机制,对处理方法的参数进行分析,从而将请求消息绑定到处理方法的参数上。数据绑定的核心是DataBinder,Spring MVC将HTTP请求的ServletRequest对象及处理方法的参数对象(通过反射)传递给DataBinder,DataBinder首先调用装配在Spring Web上下文中的ConversionServie进行数据类型转换、数据格式化等工作,将ServletRequest中的消息填充到参数对象中,然后调用Validator组件对参数对象进行数据校验,最终生成数据绑定结果BindingResult对象。BindingResult对象包含了完成数据绑定的参数对象,还包含了相应的校验错误对象(在使用JavaBean Validation时会用到)。Spring MVC抽取BindingResult对象中的参数对象及校验错误对象,赋给处理方法的相应参数。
这里写图片描述
Spring MVC提供的这种数据绑定机制,相比较于手动从HttpServletRequest对象中获取数据的方法,减少了很多重复的获取数据操作,大大提高了效率,同时也为提供非侵入式的数据校验提供了实现思路(Bean Validation/JSR 303)。

数据转换

上面提到数据绑定流程,那么具体是如何HTTP请求信息绑定到参数对象上呢?首先,从HTTP请求中获取的数据是String类型,参数对象则可以是任何类型Java对象,这很自然地想到需要实现String对象到任何类型对象的转换。Java中有一个原生的PropertyEditor类刚好可以实现这个需求,不过PropertyEditor有以下不足:

  • 只能用于字符串到Java对象的转换,不能进行任意两个Java类型之间的转换。
  • 对上下文不敏感(如注解、类结构),因此不能利用上下文信息进行高级的转换逻辑。

鉴于此,Spring实现了一个通用的类型转换模块,位于org.springframework.core.convert包中。Spring打算用这个类型转换体系代替Java原生的PropertyEditor。但由于历史原因,Spring同时支持两者。
ConversionService是Spring类型转换体系的核心,通过ConversionService接口定义的4个方法,进行类型转换。Spring通过ConversionServiceFactoryBean在应用上下文中定义一个ConversionService,并在SpringMVC方法入参等场景中使用它进行数据转换。Spring在实现ConversionService时,运用了工厂模式、适配器模式以及不在23种模式之中的注册模式,有兴趣可以去分析下源码。
在查看ConversionServiceFactoryBean源码时,发现通过实现Converter、ConverterFactory、GenericConverter中的一个接口,就可以注册自定义的类型转换器。如下图:
这里写图片描述

数据格式化

ConversionService实现了HTTP请求信息到任意类型的转换,但不提供对数据的格式化。通常源类型数据都是从客户端传递过来的,一般都是字符串,像日期、时间、数字、货币等数据为了方便阅读,都是有一定格式的,在不同的本地化环境中,同一类型数据可能会有不同的显示格式。为了从格式化的数据中获取真正的数据完成数据绑定,并将处理好的数据输出为格式化数据,就是Spring在数据绑定过程中要完成的第二个操作。
为此,Spring引入了一个格式化框架,位于context模块org.springframework.format包中。该格式化框架最重要的接口应该就是Formatter接口,该接口扩展Printer接口和Parser接口。Printer负责对象的格式化输出,Parser负责对象的格式化输入。在该包下,有一个annotation包,里面有DateTimeFormat和NumberFormat两个注解类,这大概意味着提供了基于注解的格式化功能。
那么Spring是如何将这个格式化模块加载到Spring应用上下文的呢?继续查看源码发现有一个support包,里面有一些熟悉的类,例如FormattingConversionService、FormattingConversionServiceFactoryBean等。很明显,通过配置FormattingConversionServiceFactoryBean类,从而将格式化模块加载到Spring应用上下文。事实上,SrpingMVC内部默认创建的ConversionService实例就是一个FormattingConversionService。

数据校验

在正式环境中,对输入参数进行校验是必须的。很多时候同样的数据校验出现在不同层,导致了代码冗余,违反了DRY原则。为了避免这种情况,最好的做法是将验证逻辑和域模型绑定,将验证逻辑集中管理。JSR 303定义了一套数据校验的规范,Hibernate Validator是JSR 303的一个参考实现。Spring提供了自己的数据校验框架,也支持JSR 303标准的校验框架。DataBinder在进行数据绑定时,会调用校验框架完成数据校验。
Spring校验框架位于org.springframework.validation,最核心的类是Validator接口。 该接口有两个方法:

  • boolean supports(Class<?> clazz):该校验器能够对clazz类型的对象进行校验;
  • void validate(@Nullable Object target, Errors errors):对目标对象进行校验,并将校验错误信息记录下errors中。

LocalValidatorFactoryBean实现了Spring的Validator接口,又实现了JSR 303的Validator接口。通过配置一个LocalValidatorFactoryBean,即可实现数据校验功能。Spring MVC默认会装配一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解,即可让Spring MVC在完成数据绑定后执行数据校验工作。另外,Spring MVC通过对处理方法签名的规约来保存校验结果,具体表现是一个入参对象的校验结果保存在其后的参数上,这个保存校验结果的入参必须为BindingResult或者Errors类型。也就是说,待校验对象和其校验结果对象总是成对出现的,它们中间不允许出现其他参数对象。最后,我们还可以通过自定义扩展注解实现自定义校验规则。

总结

第一次写这么长的博客,从萌生这个想法,到这篇博客完成,时间跨度大概有一周。事实上,这篇博客的内容完全是参照《精通Spring+4.x++企业应用开发实战》这本书中的内容写的,不过,即使是照着书中内容写,我也觉得写博客加深了我对这部分知识的理解。之所以时间跨度有一周,一方面我只利用了一些零碎时间写博客,另一方面亲自查看源码验证书中所说也需要时间。总之,网上流传的一句“技术人都应该有一个自己的博客”,我体会到了这句话的含义。希望能在写博客这条路上越走越远。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值