我再也不想写@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";
    }

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

相关推荐
ZhengEnCi5 小时前
S02-SpringBoot实体类新增字段对已有数据的影响及自动DDL同步机制详解
数据库·spring boot
程序员张35 小时前
SpringBoot集成BCrypt密码加密库
java·spring boot·后端
952366 小时前
RabbitMQ-基础操作
java·spring boot·分布式·后端·spring·rabbitmq
卓怡学长6 小时前
w266基于spring boot + vue 圣地延安美食乐享系统
java·数据库·vue.js·spring boot·spring·intellij-idea
Listen·Rain8 小时前
Springboot整合Ollama实现调用本地多模型
java·spring boot·后端
宠友信息9 小时前
多端即时通讯源码技术实践,Redis路由、MySQL持久化与Socket接入
spring boot·websocket·mysql·uni-app
Tom·Ge9 小时前
【Spring Boot 4】Spring Boot 4.0 AI Agent实战:Skills Agent + MCP协议集成
人工智能·spring boot·microsoft
Devin~Y9 小时前
电商场景下的Java面试实战:从Spring Boot微服务到Kafka、Redis与AI RAG
java·spring boot·redis·elasticsearch·spring cloud·微服务·kafka
白露与泡影10 小时前
用 Spring Boot + Vue + Fuzio 构建现代 Java 桌面应用
java·vue.js·spring boot
宠友信息11 小时前
Spring Boot与异步审核构建仿小红书源码内容发布全流程
java·数据库·spring boot·redis·mysql·oracle·uni-app