由于mybatis原生的分页是在内存里面进行的,导致效率很低,但是我们在生产项目中有很多的分页需求,这个时候PageHelper分页插件就诞生了。PageHelper主要是通过插件拦截链实现的。
我们知道在创建StatementHandler 的时候,我们包装了interceptorChain链
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
//给某个对象添加插件拦截器链
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
那这里如何初始化interceptors呢?
我们知道在解析配置文件的时候有一个解析plugin的方法:pluginElement(root.evalNode("plugins"));
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//解析每一个插件拦截器
for (XNode child : parent.getChildren()) {
//获取配置信息
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//添加参数
interceptorInstance.setProperties(properties);
//添加插件拦截器到configuration中
configuration.addInterceptor(interceptorInstance);
}
}
}
//添加参数 这里看PageInterceptor
public void setProperties(Properties properties) {
//缓存 count ms
msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
String dialectClass = properties.getProperty("dialect");
if (StringUtil.isEmpty(dialectClass)) {
//这里如果没有配置dialect 走默认的com.github.pagehelper.PageHelper
dialectClass = default_dialect_class;
}
try {
Class<?> aClass = Class.forName(dialectClass);
dialect = (Dialect) aClass.newInstance();
} catch (Exception e) {
throw new PageException(e);
}
dialect.setProperties(properties);
String countSuffix = properties.getProperty("countSuffix");
if (StringUtil.isNotEmpty(countSuffix)) {
this.countSuffix = countSuffix;
}
try {
//反射获取 BoundSql 中的 additionalParameters 属性
additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
additionalParametersField.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new PageException(e);
}
}
//添加插件拦截器到configuration中
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
然后我们看一下pagehelper是如何实现的
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class PageInterceptor implements Interceptor
首先我们发现这里必须是实现了Interceptor接口,这样才可以配置在plugin标签里
然后我们看一下interceptor.plugin(target);方法
public Object plugin(Object target) {
//TODO Spring bean 方式配置时