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

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章