1. 前言
PageHelper我们都不陌生,经常在项目中用作分页,它的用法也很简单,直接用PageHelper.startPage就可以完成分页了,那么PageHelper底层是怎么帮助我们实现分页功能的呢?
在进入源码的剖析之前先猜想一下PageHelper的原理:PageHelper通过在sql最后面拼接limit从而实现分页,在这里初步推测是拦截了query方法,把原来的MapperStatement替换成了新的MapperStatement,新的MS有分页信息。我们都知道mybatis底层执行查询操作的时候,其实是通过Executor.query去完成的,那么这里进一步猜测,是为executor生成了代理类,代理类在MS中增加了分页信息。
2. 源码解析
有了上述的猜测,下面就带着问题进入PageHelper的源码。分析源码最好的方式是一步步debug进去看看,每一步的背后都发生了什么,这里以平时最常使用的一种方法作为切入点:
public List<ProjectSpace> getSpace() {
// 返回第一页的前十条数据
PageHelper.startPage(1, 10);
return spaceMapper.selectByExample(new ProjectSpaceExample());
}
点击进入,是一连串构造方法的调用,大概就是设置查询之前是否需要先做count操作、入参不合理的时候是否进行自动的参数合理化等,到最后可以来到:
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 = SqlUtil.getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
SqlUtil.setLocalPage(page);
return page;
}
第一行代码相对比较简单,就是生成一个Page对象,并为它赋值;PageHelper中Page都是存储在ThreadLocal里的,这样做的好处是两个线程互不影响,所以在这里需要将新生成的Page对象放入到该线程的ThreadLocal里。
到这里可能会有点疑惑,PageHelper执行了之后好像没有什么有效信息啊,都是一些简单的赋值操作。当分析不下去的时候可以看看这个功能的使用是不是还依赖了别的配置,果然,我们发现定义了一个PageHelper的Bean。
@Bean("onePageHelper")
public PageHelper onePageHelper() {
PageHelper pageHelper = new PageHelper();
// 一些属性的set
return pageHelper;
}
查看PageHelper的UML类图发现,PageHelper是Interceptor的子类,这个方法有两个主要方法,一个是intercept,一个是plugin,看着方法可以推测一下,plugin是把目标包装成一个代理类,然后执行特定方法的时候调用的是这个代理类的intercept。那什么时候会用到这些Interceptor呢

本文详细解读了PageHelper如何通过Interceptor实现分页功能,涉及源码调试、Executor拦截和ThreadLocal的使用,以及如何自定义Interceptor进行数据脱敏处理。
最低0.47元/天 解锁文章
5万+

被折叠的 条评论
为什么被折叠?



