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";
    ...
}

在这里插入图片描述

发布了431 篇原创文章 · 获赞 329 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/No_Game_No_Life_/article/details/105391744