我再也不想写@RequestParam("current")了

起因

众所周知,分页表格属于是各种web系统的常客,系统中每个功能几乎都至少对应了一个分页表格。

所以当我在写各种Controller时,敲下的第一个接口往往是/xxx/page,同时总是需要接上各种分页参数

java 复制代码
@GetMapping("/xxx/page")
public Page<User> page(@RequestParam("current") Integer current,
                       @RequestParam("size") Integer size,
                       @RequestParam(value = "column", required = false) String column,
                       @RequestParam(value = "is_asc", required = false) Boolean isAsc,
                       @RequestParam(value = "keyword", required = false) String keyword) {
    // xxx
    return page;
}

真是公公又式式啊,不过作为一个二手代码搬运工,这样公式化的接口之前都是直接cv的,方便快捷不用过脑。不过最近闲下来了,回顾之前的代码,看着这一长串的参数接收,越看越难受。

思路

首先想解决的问题是在Controller层要接收的参数太多了,且部分参数基本固定,在查询时我还需要手动把他们转换成一个Page对象作为查询入参(MybatisPlus)

思考了一下常见的分页表格,一般由2部分组成。

  • 固定部分:分页参数(页号、页码、排序列、正序倒序)
  • 不固定部分:筛选条件(关键词搜索,其他各种筛选)

筛选条件的部分各个功能都不一样,封装太难统一了,这次不如就把固定部分封装一下好了,一下干掉4个参数,这样传参的时候也能清爽很多。并且通常业务不存在需要2个列同时进行排序,那就直接写死,1个参数放排序列名,1个参数放 正序/倒序

不要问我为什么不直接用post请求放body里,哥们表格从来就是get请求,祖宗之法不可变~

开搞

定义统一的参数对象

java 复制代码
@Data
public class PageParams {
    private Integer current;
    private Integer size;
    private String column;
    private Boolean isAsc;

    public <T> IPage<T> toPage() {
        Page<T> page = Page.of(this.current, this.size);
        if (this.column != null) {
            boolean isAsc = this.isAsc == null || this.isAsc;
            page.addOrder(new OrderItem(this.column, isAsc));
        }
        return page;
    }
}

这里额外除了定义分页参数,也加了一个通用的方法,用于把这几个参数转换成MybatisPlus的分页参数对象,这样查询的时候就能直接传参了

自定义注解

java 复制代码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PageParam {

    String currentName() default "current";

    String sizeName() default "size";

    String columnName() default "column";

    String isAscName() default "is_asc";
}

要让Spring更准确的注入参数,也减少引发bug的概率,自定义注解是必不可少的

虽然通常这几个参数的名称都是一样的,但是为了防止需求方突然犯病,还是留下了自定义参数名称的地方

参数注入接口

java 复制代码
public class PageParamArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        if (!parameter.hasParameterAnnotation(PageParam.class)) {
            return false;
        }
        Class<?> type = parameter.getParameterType();
        return type.isAssignableFrom(PageParams.class) || type.isAssignableFrom(Page.class);

    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        PageParam anno = parameter.getParameterAnnotation(PageParam.class);
        if (anno == null) {
            return null;
        }
        Class<?> type = parameter.getParameterType();

        Integer current = getIntParamNotNullByWebRequest(anno.currentName(), webRequest);
        Integer size = getIntParamNotNullByWebRequest(anno.sizeName(), webRequest);
        String column = webRequest.getParameter(anno.columnName());
        String isAsc = webRequest.getParameter(anno.isAscName());

        PageParams pageParam = new PageParams();
        pageParam.setCurrent(current);
        pageParam.setSize(size);
        if (StringUtils.isNotBlank(column)) {
            pageParam.setColumn(column);
            // 默认是升序排列
            pageParam.setIsAsc(!Boolean.FALSE.toString().equalsIgnoreCase(isAsc));
        }
        if (type.isAssignableFrom(Page.class)) {
            return pageParam.toPage();
        }
        return pageParam;
    }

    private Integer getIntParamNotNullByWebRequest(String name, NativeWebRequest webRequest) {
        String value = webRequest.getParameter(name);
        if (StringUtils.isBlank(value)) {
            throw new IllegalArgumentException(String.format("参数: %s 为空", name));
        }
        return Integer.parseInt(value);
    }
}

一顿搜索之后发现Spring很贴心的为我们准备了 HandlerMethodArgumentResolver接口实现参数的注入.

这里我打算支持2种参数,我们自定义的分页参数和MybatisPlus的分页参数都支持,进一步减少一行参数转换

纯业务需求默认必传页号和页码,不想加也行

添加WebConfig

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new PageParamArgumentResolver());
    }
}

大功告成!!!

java 复制代码
    @GetMapping("/params1")
    public String params1(@PageParam PageParams pageParams) {
        return "hello";
    }

    @GetMapping("/params2")
    public String params2(@PageParam Page<User> page) {
        return "hello";
    }

使用的时候直接用注解+分页参数,完美解决

相关推荐
计算机毕设VX:Fegn08958 分钟前
计算机毕业设计|基于springboot + vue球鞋购物系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
苏渡苇9 分钟前
用 Spring Boot 项目给工厂装“遥控器”:一行 API 控制现场设备!
java·人工智能·spring boot·后端·网络协议·边缘计算
像少年啦飞驰点、13 分钟前
零基础入门 Spring Boot:从“Hello World”到独立可运行 Web 应用的完整学习闭环
java·spring boot·web开发·编程入门·后端开发
yangminlei18 分钟前
Spring Boot 4.0.1新特性概览
java·spring boot
她说..43 分钟前
策略模式+工厂模式实现订单校验功能
java·spring boot·java-ee·简单工厂模式·策略模式
没有bug.的程序员2 小时前
Spring Boot 与 Swagger:API 文档自动化生成、版本管理与云原生协作深度实战指南
spring boot·云原生·自动化·api·swagger·版本管理·自动化生产
mqiqe2 小时前
springboot tomcat 嵌入式 解决Slow HTTP DOS问题解决
spring boot·http·tomcat
洛阳纸贵2 小时前
JAVA高级工程师--Springboot集成ES、MySQL同步ES的方案、ES分片副本、文档及分片规划
java·spring boot·elasticsearch
我是一只小小鱼~2 小时前
JAVA 使用spring boot 搭建WebAPI项目
java·数据库·spring boot
小信丶2 小时前
@EnableMethodCache 注解详解:原理、应用场景与示例代码
java·spring boot·后端·spring