PageHelper 是Mybaties中的一个分页插件。其maven坐标
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version> </dependency>
目前以更新到5.1 ,新的版本好像和以前的版本有些差别 这里分析下以前的版本
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.1.3</version> </dependency>
其和Mybaties的配置整合不累赘。只看看如何实现分页的原理。
先说如果在代码中使用 ,很简单如下,一行代码 ,在查询的时候就自动实现分页:
PageHelper.startPage(params.getPageIndex(), params.getPageSize(), true, true);
其startPage方法重载 :
跟踪代码 进入到PageHelper这个类 看到这个类继承了Interceptor 接口,这个接口是Mybaties 提供的
public class PageHelper implements Interceptor { //sql工具类 private SqlUtil sqlUtil; //属性参数信息 private Properties properties; //配置对象方式 private SqlUtilConfig sqlUtilConfig; //自动获取dialect,如果没有setProperties或setSqlUtilConfig,也可以正常进行 private boolean autoDialect = true; //运行时自动获取dialect private boolean autoRuntimeDialect; //多数据源时,获取jdbcurl后是否关闭数据源 private boolean closeConn = true; //缓存 private Map<String, SqlUtil> urlSqlUtilMap = new ConcurrentHashMap<String, SqlUtil>();
...
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties); }
可以看到 Interceptor 类中有个拦截方法intercept ,这个方法就应该是拦截器的执行方法 。继续跟踪这个方法在哪里有引用 。发现只在类plugin类有引用
可以看到在method.invoke 执行之前 ,拦截器执行了拦截方法 ,而 pageHelper这个类就是个拦截器 。再看看Plugin这个类 实现了动态代理 的接口 InvocationHandler 。其私有构造方法在静态方法wrap中调用 ,说明这能通过wrap这个方法获取Plugin实例,wrap方法对参数target做了个动态代理 。所以说target执行方法是通过代理 Plugin 执行invoke方法 。
那么wrap方法中的target 又是什么呢 ???。wrap方法又是在什么时候调用的呢 ?一层一层的跟踪 如下代码 。
又回到了 pageHelp 这个类,在类plugin方法中调用了wrap方法,而且target只有在属于Executor 的时候把target做了代理。跟踪代码发现Plugin方法又在拦截器链InterceptorChain方法pluginAll调用
pluginAll 就是把target这个类按拦截器的数量做了多次对plugin的代理 ,而每个plugin中都有一个不同的拦截器 Interceptor。而target在这可以把它 看做是执行器Executor 了 。因为前面说过只对Executor 拦截,再看看pluginAll方法的引用, 在类 Configuration如下
可以看到在Configuration创建 Executor 、ParameterHandler、ResultSetHandler、StatementHandler 都有对做代理 ,对拦截器进行拦截执行。但是pageHelp只对Executor 做了代理 。回过头来看看拦截器PageHelp做了什么 ?
/** * Mybatis拦截器方法 * * @param invocation 拦截器入参 * @return 返回执行结果 * @throws Throwable 抛出异常 */ public Object intercept(Invocation invocation) throws Throwable { if (autoRuntimeDialect) { SqlUtil sqlUtil = getSqlUtil(invocation); return sqlUtil.processPage(invocation); } else { if (autoDialect) { initSqlUtil(invocation); } return sqlUtil.processPage(invocation); } }
方法最终就是通过sqlUtil在执行sql中加了分页参数 。完成分页 。