Java架构直通车——结合源码理解PageHelper

PageHelper实现方式?

  1. PageHelper首先将前端传递的参数保存到page这个对象中,接着将page的副本存放入ThreadLoacl中,这样可以保证分页的时候,参数互不影响。
  2. 接着利用了mybatis提供的拦截器,取得ThreadLocal的值,重新拼装分页SQL,执行查询的时候通过拦截器在sql语句中添加分页参数,之后实现分页查询。
  3. 最后在finally代码段中自动清除了ThreadLocal存储的对象,防止内存泄漏。

第一步:在ThreadLocal中保存Page

public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
    ...

	public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
		//page对象存储了如pageNum、pageSize、orderBy等查询条件
        Page<E> page = new Page(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }

        setLocalPage(page);//存储到ThreadLocal中
        return page;
    }

	protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

Debug到这个位置,其实Page.startpage已经完成了它应该做的,接下来就是执行selectByPrimaryKey()这个方法了。
在这里插入图片描述

第二步:SqlSessionFactory注入Interceptor

继续Debug下一句selectByPrimaryKey(),我们来到了这个类:

public class MapperProxy<T> implements InvocationHandler, Serializable {
	...
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }
}

先不管来到的哪个方法,光是看InvocationHandler就能明白,这是个动态代理、。

在这个类里,Mybatis完成了初始化的过程:
在这里插入图片描述
初始化的的核心流程就是 读取配置文件 到 Congiguration实例,之后生成全局公用的 SqlSessionTemplate 以SqlSessionFactory实例。

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
	...
	private Interceptor[] plugins;

	protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
		...
		if (!ObjectUtils.isEmpty(this.plugins)) {
            Stream.of(this.plugins).forEach((plugin) -> {
                targetConfiguration.addInterceptor(plugin);
                LOGGER.debug(() -> {
                    return "Registered plugin: '" + plugin + "'";
                });
            });
        }
		...
	}
}

在SqlSessionFactoryBean中,我们注意到,其属性包含了Interceptor数组,而buildSqlSessionFactory()方法通过在Configuration加入plugins,完成Interceptor的注入。

初始化完成后,接下来是Mapper的查询流程,是一个调用链,如下:
在这里插入图片描述
核心是 configuration.newExecutor()方法,会加载拦截链,也就是pageInterceptor。

第三步:PageInterceptor实现分页

PageHelper最核心的逻辑在 PageInterceptor 中,PageInterceptor 是一个拦截器。

public class PageInterceptor implements Interceptor {
	private volatile Dialect dialect;
    private String countSuffix = "_COUNT";
    protected Cache<String, MappedStatement> msCountMap = null;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";
    ...
}

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值