PageHelper插件原理

介绍

PageHelper是一个很好的mybatis分页插件。经过简单的配置,只需要几行代码就可以实现分页查询。

数据库数据如下:

 @RequestMapping("/findAllUser")

public ResponseResult<PageBean> findAllUser2(PageModel pageModel) {
  PageHelper.startPage(pageModel.getPageNum(), pageModel.getPageSize());
  PageHelper.orderBy("id desc");
  List<MyUser> myUsers = myUserServices.selectUser();
  PageBean<MyUser> pageBean = new PageBean<>(myUsers);
  return new ResponseResult<>(200, "success", pageBean);
}

代码看起来比较奇怪,只需要传递进来需要的当前页面数已经每页的大小就可以实现对mybatis的查询语句分页;另外还可以实现升序和降序。

  1. 它是如何办到的?是否使用了动态代理?
  2. 是通过sql实现分页吗?
  3. PageHelper.startPage()是不是使用了ThreadLocal?什么时候释放ThreadLocal里面的value?是否会造成ThreadLocal内存泄漏?

带着这些问题我进行了源码分析,本文不介绍如何配置实现分页功能,代码可以从这里下载: https://github.com/pmh905001/freedom-20200203springboot

主要还是探究PageHelper的实现原理,可以从这篇文章借鉴到源码分析的一些思路.

入手

如何入手分析PageHelper的实现原理呢?

当前代码是controller--->service--->Mapper就简单几行代码是无法看到跟踪到内部实现的,排除此方法。

它的代码里面既然设置了pageSize, PageHelper.startPage(pageModel.getPageNum(), pageModel.getPageSize());那么就一定有使用它的地方,我们顺着这个方法可以跟踪到这里

  

 PageHelper原理分析

 看到这里实际上回答我的第三个问题:PageHelper.startPage()是不是使用了ThreadLocal?是的,它是使用了ThreadLocal。

那么我们就在我们就在PageSize这个字段做一个跟踪断点。我们可以得到如下调用栈:

 我们可以找到一个名为PageInterceptor的代理类对正在执行的mybatis SQL进行了拦截,首先查询的总数,然后在进行分页查询。 

并且从下面的语句我们可以看出这个动态代理类拦截的是Executor类里面的query方法。

分析到这里,实际上已经回答了我的第一个疑问 。

  1. 它是如何办到的?是否使用了动态代理?

他是通过动态代理拦截了Executor.query()方法。而且他应该java自带的动态代理实现。

通过分析PageInterceptor,我们同样可以回答问题2:

  2.是通过sql实现分页吗?

是通过sql来实现分页的,这里不再详述。

 其他

还有一个问题是我们看到了PageHelper是通过ThreadLocal来设置分页信息的,那么什么地方该来释放分页信息呢?否则有可能造成内存泄漏.

PageHelper里面有一个clearPage()方法,反向查看调用点即可以知道程序在哪儿释放分页信息。

从上面的代码可以看出,拦截器执行完毕之后最终会释放分页信息。并且放在finally块中,可以保证不会出现ThreadLocal内存泄漏。

还有一个问题,Mybatis是否支持Executor多个拦截器,比如:分页拦截器和排序拦截器,答案是肯定的。Mybatis使用提供了一种插件的机制,读者可以参考一下InterceptorChain.pluginAll()方法。

 这里代码有点绕,我之前看到这里感觉很别扭,这应该只返回一个动态代理类,怎么会支持多层拦截呢? 实际上这里代码写的很精炼,每一次循环相当于被代理上面套了一层,第二次循环就会再在动态代理类基础上再封装一层.

猜你喜欢

转载自www.cnblogs.com/pmh905001/p/12286008.html