在Mybatis中,我们经常会遇到分页的问题,但是很少有人会去手动写分页的sql语句,一般都是使用pageHelper或者MybatisPlus等第三方插件,那么究竟底层mybatis是如何做的呢,看下源码就知道。
首先可以想到的是,既然需要帮助开发人员进行分页,那么就需要有拦截器才行,那在哪里拦截好呢,肯定是执行sql的地方了,Mybatis底层也是jdbc,所以既然是jdbc,那就有获取sql,执行sql的地方了。我们只需要在执行sql的地方把sql语句拦截到,在原sql基础上修改就可以了。
说下拦截器:
// 一个拦截器框架
// type 拦截的地方
// method 拦截的方法,参数为
// args 方法的参数 确定是哪一个方法
@Intercepts({@Signature(type=Executor.class,method="query",args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class })})
public class PageInterceptor implements Interceptor{
// 执行的拦截逻辑
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
// 判断验证是否是需要拦截的
public Object plugin(Object arg0) {
return Plugin.wrap(arg0, this);
}
// 拦截器传的参数
public void setProperties(Properties arg0) {
}
}
mybatis配置文件中配置拦截器
<plugins>
<plugin interceptor="cn.com.dingding.common.utils.PageInterceptor">
</plugin>
</plugins>
下面看下原理
// 这是加载过程
会在parseConfiguration中解析配置文件中的plugin节点
// 解析 plugins 节点
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).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
// 放入配置类的interceptor中
configuration.addInterceptor(interceptorInstance);
}
}
}
// 构建过程 这里只看执行器的 configuration类中,搜索一下interceptorChain.pluginAll,就能找到其他的
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// ....省略 看重点
// 创建Executor代理对象
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
// 加载配置的时候set的值
private final List<Interceptor> interceptors = new ArrayList<>();
// 循环处理拦截器链
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
// 不断的对executor进行代理
// 此时就会调用刚刚开始的例子处的plugin方法了,因为那个方法实现了Interceptor接口
target = interceptor.plugin(target);
}
return target;
}
//
@Override
public Object plugin(Object target) {
// 对target进行包装
return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 这里进行代理对象创建
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
// 执行Plugin类的invoke方法
new Plugin(target, interceptor, signatureMap));
}
return target;
}
// 看下具体执行的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
// 此处调用了自定义拦截器的interceptor
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
// ...
// 反射调用拦截器
return invocation.proceed();
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}