粗解PageHelper分页逻辑——分页参数的获取与传递(线程局部变量)

举报
KevinQ 发表于 2022/01/09 22:02:02 2022/01/09
【摘要】 PageHelper对线程局部变量的妙用

起因

在学习若依管理系统的过程中,发现其系统的几乎没有分页的代码,很是疑惑。发现其使用了一个名为PageHelper的中间件,通过实现mybatis拦截器,拦截执行分页SQL,从而来实现后台分页功能,那么这个PageHelper的实现原理是什么样的?我们来粗略的探究一下。

参数获取与传递与保存

在学习的过程中,发现若依系统中出现分页时,在Controller层并没有编写如下获取分页参数的方法,例如:

@PreAuthorize("@ss.hasPermi('monitor:oper:list')")
@GetMapping("/list")
public TableDataInfo list(SysOper oper) {
	startPage();
   // ....
}

那么分页参数是如何获取的呢?又是如何传递给后续SQL的呢?

我们发现每次分页都与startPage()方法有关,猜测分页参数大概是在startPage()函数中获取的。看一下startPage源码:

/**
     * 设置请求分页数据
     */
    protected void startPage()
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
        {
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            Boolean reasonable = pageDomain.getReasonable();
            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
        }
    }

可以清楚地看到其中有两行:

Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();

查看其基础逻辑为通过TableSupport.buildPageRequest()构造一个PageDomain对象,其中通过:

getRequest().getParameter(name);

达到获取请求中的分页参数的目的。

那么后续分页参数是如何传递的呢?

作者使用了一个非常巧妙的对象:ThreadLocal。通过使用ThreadLocal保存分页的参数,以便后续使用。这个对象通常被称为线程局部变量

我们来看一下逐层的逻辑,上述的startPage在最后一句调用:

PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);

其方法内部为:

/**
 * 基础分页方法
 *
 * @author liuzh
 */
public abstract class PageMethod {
    /**
     * 开始分页
     *
     * @param pageNum      页码
     * @param pageSize     每页显示数量
     * @param count        是否进行count查询
     * @param reasonable   分页合理化,null时用默认配置
     * @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);
        return page;
    }
}

其中的关键是getLocalPage以及setLocalPage,我们来看一下这两个方法:

/**
 * 基础分页方法
 *
 * @author liuzh
 */
public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
    
    /**
     * 设置 Page 参数
     *
     * @param page
     */
    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

    /**
     * 获取 Page 参数
     *
     * @return
     */
    public static <T> Page<T> getLocalPage() {
        return LOCAL_PAGE.get();
    }
}

在使用时,即可通过getLocalPage()方法获取到分页相关的参数。

总结

线程局部变量之前我只在阿里巴巴的日期格式化编码规范中见过,没想到线程局部变量的功能会如此强大与巧妙。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。