在Spring中,如果在方法参数列表中使用_@RequestParam_标注多个参数,会让映射方法的可读性大大降低。
如果映射请求的参数只有一两个的话,使用_@RequestParam_会非常直观,但是如果参数列表越来越长,就很容易晕菜。
虽然我们不能直接在参数对象中使用_@RequestParam_标签,但是并不代表没有其他的办法。这篇文章就会演示怎么使用对象的封装来简化多个@RequestParams标签。
【注:SpringMVC注入请求参数到对象中,这个对于很多开发是再正常不过的,但是这里强调的是使用@RequestParam来绑定参数,因为@RequestParam可以对绑定参数有更多的限制】
不管是controller还是其他的类,过长的参数列表会让代码的可读性变差,这一点是所有开发人员都认同的。更不要说,如果大量的参数的类型还是一致的情况下,参数就更容易混淆了。
很多代码检查工具,都会把方法的参数个数作为检查条件,也是因为过长的参数列表被认为是一种错误的代码规范。
常见的一种解决方案,就是把一组参数合并起来,并作为应用的独立的一层。常见的,这组参数可以合并到一个对象中,并给予这个对象一个恰当的名字即可。
我们来看一个GET请求服务端的例子:
@RestController
@RequestMapping("/products")
class ProductController {
//...
@GetMapping
List<Product> searchProducts(@RequestParam String query,
@RequestParam(required = false, defaultValue = "0") int offset,
@RequestParam(required = false, defaultValue = "10") int limit) {
return productRepository.search(query, offset, limit);
}
}
虽然该方法只有三个参数,但是参数列表很容易增长的,比如既然代码中是查询商品服务,那么常常需要包含按照一些额外的过滤条件进行排序等操作。在我们的代码中,因为参数是直接传递给数据连接层,所以我们可以直接使用ParameterObject模式来处理【注:ParameterObject就是把参数组装成对象】。
根据我的经验,很多开发没有替换较长的_@RequestParams_列表,主要还是因为他们不知道有什么替代的方案,因为在Spring的文档中没有提及。
下面,我们就开始来阐述替换的方案。首先我们可以使用一个POJO来包装这些参数。
@GetMapping
List<Product> searchProducts(ProductCriteria productCriteria) {
return productRepository.search(productCriteria);
}
就已经完成了!
这个POJO本身没有要求额外的注解,但是POJO本身必须包含和请求参数完全匹配的字段,标准的setter/getter,和一个无参的构造器:
class ProductCriteria {
private String query;
private int offset;
private int limit;
ProductCriteria() {
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
// other getters/setters
}
虽然上面的案例已经可以正常使用,但是我们知道,使用@RequestParam注解,不仅仅只是为了绑定请求参数,一个非常重要的功能是,我们可以对绑定的参数请求验证,比如参数是否必要,如果请求中缺少该参数,则我们的服务端可以拒绝该请求。
为了达到相同的功能,我们常常使用的替换方案是使用Java Bean Validation。java有很多内置的实现,我们也可以创建自己的bean验证器。
回到我们的POJO,我们想为我们的POJO中的字段添加验证规则。如果想模仿@RequestParam(required = false)的表现,我们可以使用@NotNull注解在对应的字段上即可。
在更多的情况下,我们一般使用@NotBlack多于@NotNull,因为@NotBlank考虑了空字符串的情况。
final class ProductCriteria {
@NotBlank
private String query;
@Min(0)
private int offset;
@Min(1)
private int limi;
// ...
}
这里务必注意一点:
如果仅仅只是在对象的字段上添加验证注解是不够的。
一定要在controller的方法参数里诶包中,在POJO对应的参数前加上@Valid注解。该注解会让Spring在绑定参数前执行校验动作。
@GetMapping
List<Product> searchProducts(@Valid ProductCriteria productCriteria) {
// ...
}
@RequestParam注解的另一个非常有用的功能就是设置参数的默认值。
如果我们使用POJO的方式来绑定参数,没有什么特别牛逼的方法,只需要在定义参数的时候设置好字段的默认值就行了。如果请求中没有该参数,Spring不会把参数的默认值覆盖为null的。
private int offset = 0;
private int limit = 10;
一般情况下,我们也不会强行把所有请求参数全部封装到一个对象中,我们可以把请求参数按照功能分布到多个POJO中。
为了验证这点,我们在查询方法中,添加一个排序的功能。首先,我们需要一个额外的对象,并添加一些校验约束:
final class SortCriteria {
@NotNull
private SortOrder order;
@NotBlank
private String sortAttribute;
// constructor, getters/setters
}
在controller中,我们只需要把这个POJO作为另一个参数即可。但是仍然注意,想让校验生效,还是需要在参数对象前添加@Valid注解。
@GetMapping
List<Product> searchProducts(@Valid ProductCriteria productCriteria, @Valid SortCriteria sortCriteria) {
// ...
}
另一种处理请求参数对象的方式是使用组合。参数绑定对这种内嵌对象同样适用。
下面我们给出一个改进的例子,把查询对象移动到ProductCriteria中。
要让内置对象的字段能够执行验证,我们需要在内置对象对应的字段上添加@Valid注解。注意一点的是,如果这个内置对象的字段是null,Spring是不会校验这个属性的,这可以简单理解为,所有的内置对象属性都是可选的。如果想避免这种情况,在内置对象的字段上添加@NotNull注解。
final class ProductCriteria {
@NotNull//注意这个@NotNull注解
@Valid
private SortCriteria sort;
// ...
}
HTTP请求参数必须按照参数路径的方式命名。比如我们的例子中,请求参数就必须是:
sort.order=ASC&sort.attribute=name
现在,我们发现一个趋势,越来越多的开发会把传统的POJO中的setter方法去掉,让POJO变成一个不可变对象。
不可变对象有很多的好处,但是在我看来,最大的优势在于便于维护。
在你的开发中,是否有这样的情况,开着debug,在整个应用大量的代码中,去追踪一个对象的状态是怎么变化的?在什么地方,一个状态发生了什么样的变化?什么情况下,这个对象状态需要修改?对于很多对象来说,仅仅从setter方法的名字来看,是很难看出具体的业务逻辑的。
当Spring框架刚被创建出来的时候,是严格按照Java Bean规范来开发的。但是,时至今日,很多过去推崇的模式,已经变成了反模式。
要去掉setter方法,绑定请求参数,有两种方式,通过构造器或者字段直接绑定。但是目前没有一种非常简单的办法,直接通过构造方法将请求参数绑定,因为默认的构造方法是必须的。虽然我们可以把POJO的构造方法变为private的,并移除掉setter方法,从外部访问来看,确实变成了私有的,但是这种方式有一定的缺陷,比如内部组合对象无法使用这种方式。
默认情况下,Spring要求通过字段的setter方法来绑定参数,但是我们可以通过自定义的绑定器(binder)来直接把请求参数通过字段绑定。
为了可以在我们整个应用中,都使用这种绑定方式,我么你可以定义一个controller advice组件。通过_@InitBinder_ 注解,来修改默认的绑定请求参数方法。
@ControllerAdvice
class BindingControllerAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.initDirectFieldAccess();
}
}
当我们创建好这个类之后,我们就可以把POJO中所有的setter方法去掉,让我们的POJO成为不可变对象。
final class ProductCriteria {
@NotBlank
private String query;
@Min(0)
private int offset = 0;
@Min(1)
private int limit = 10;
private ProductCriteria() {
}
public String getQuery() {
return query;
}
public int getOffset() {
return offset;
}
public int getLimit() {
return limit;
}
}
在本文中,我们总结了在Spring MVC控制器中使用参数对象来简化过长的_@RequestParam_参数列表绑定;并讨论了如果使用不可变的DTO对象来替换简单的POJO。
原文:https://www.javacodegeeks.com/2018/10/how-bind-requestparam-object-spring.html
Original url: Access
Created at: 2020-06-29 11:20:05
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论