没事看一下mybatis插件源码, 虽然之前时知道原理的,但是时间长了都记不清了, 所以这次直接翻源码看一下, 重新了解一下原理。
SqlMapperConfig.xml中配置了两个插件
在执行SqlSessionFactory的OpenSession时,需要创建Executor执行器
这里查看一下newExecutor方法
发现虽然创建了Executor,但是返回的时interceptorChain.pluginAll包裹的方法,看一下这个interceptorChain.pluginAll干了什么
ok,这里所有的自定义插件被实例化了,放到interceptors中,然后Executor对象创建后要被所有的自定义插件执行plugin方法包裹一下,这个interceptor.plugin就是我们自定义插件里的plugin方法,一般返回的都是Plugin.wrap()
如果不写的话默认也是Plugin.wrap()
然后再看一眼这个wrap()方法干了啥
原来是对创建的Executor执行器创建了代理,然后看一下具体的执行
Plugin类是实现了InvocationHandler接口的,熟悉jdk动态代理的都知道,调用代理类的方法,就是执行这个接口实现类的invoke方法,而代理类的创建就是再wrap方法中创建的。再看下方的invoke方法,这个是调用逻辑,内部执行了interceptor。intercept方法,就是我们自定义插件的内部逻辑,ok,到此插件的原理就搞明白了,就是创建了层层的代理类,然后执行方法时执行我们自定义插件方法的intercept方法逻辑。
Executor创建后返回的时代理类,那StatementHandler,ParameterHandler,ResultHandler呢?肯定也是,随便翻一个出来看一下
嗯,也是!
这样我们就可以自定义插件,再根据需求改执行的sql了。
下面自定义一个插件试试:
@Intercepts({
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class,Integer.class}
)
})
public class MyPageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String newSql = boundSql.getSql()+" limit 0,2";
Field sqlField = boundSql.getClass().getDeclaredField("sql");
sqlField.setAccessible(true);
sqlField.set(boundSql,newSql);
return invocation.proceed();
}
}
尝试效果:
其实可以翻看一下com.github.pagehelper.PageInterceptor这个类,这个是github开源的分页插件,别人是如何用ThreadLocal缓存分页变量,查询count统计,执行分页逻辑,封装分页结果的。