在上一篇文章,我们介绍了如何在 MyBatis 中编写一个插件,本篇我们就来看看插件的执行原理。来思考一个问题, 不改变原有代码,怎么改变和增强对象的行为?答:代理模式。
如果是用代理模式,那么,有哪些对象允许被代理?有哪些方法可以被拦截? 上面已经说过了,我们再来看一下:
1.代理对象何时创建?
那么问题又来了,这四大对象什么时候被代理的,或者说代理对象是什么时候创建的?
Executor 是在我们调用 SqlSessionFactory#openSession() 时就会创建(因为 Executor 是 SqlSession 的一个核心成员变量)
来看 Configuration 的 newExecutor 方法:
对于另外三个核心对象,是在具体执行 SQL 时创建的,StatementHandler 是 SimpleExecutor.doQuery() 创建的,里面包含了处理参数的 ParameterHandler 和处理结果集的 ResultSetHandler 的创建;创建之后即调用 InterceptorChain.pluginAll(), 返回层层代理后的对象。
2.代理对象如何创建的?
继续来看 InterceptorChain#pluginAll():
可以看到,它是对传进来的 target 不断调用每个 Interceptor 的 plugin() 方法层层创建代理。
PS:这里其实也可以理解成责任链模式,即多个插件的情况下,为 pluginInterceptors 中的每个插件都成一个代理,然后代理再被代理。其中,创建代理对象的顺序是我们按照我们配置的顺序,而代理方法的执行逻辑则是相反顺序。
上面我们说过,插件需要自己实现 plugin() 方法,来定义如何生成代理类,下面我们就来看看 PageHelper 的 plugin 方法的实现:
可以看到,PageHelper 的 plugin 方法是直接调用了 MyBatis 提供的 Plugin 类的 wrap方法,

谜团终于揭晓了,原来 MyBatis 插件的代理对象是通过 JDK 动态代理生成的。
PS:这里强调一下,我们并没有直接编写代理类,而是 MyBatis 替我们实现了代理逻辑(Plugin)。
3.方法如何执行?
在上面我们看到了,在创建 JDK 动态代理时,传入的是一个新的 Plugin 实例,所以实现 InvocationHandler 接口的就是 Plugin 类本身

那么,当调用 Executor 去执行 SQL 时,就会走到 Plugin#invoke() 方法:

总结一下,调用逻辑是:代理类 => Plugin#invoke() => Interceptor#intercept(),所以再强调一次,调用到我们自己的插件逻辑时,实际已经是第三层了。
问题:怎么调用到原被代理对象的方法?
可以从上面看到,在我们每次调用插件的 intercept() 方法时,都会构造一个 Invocation 对象,而这个对象封装了
- target:被代理的原对象(比如 SimpleExecutor)
- method:当前要执行的方法
- args:当前方法的参数

所以,可以通过 Invocation#proceed() 调用到被代理对象被拦截的方法。
注:虽然可以通过 proceed() 执行原方法,但是拿分页来说,它需要执行的新的 sql,所以很多情况下,我们不是直接调用 proceed(),而是在 Invocation 中拿出 target(比如 Executor)去直接执行新 sql。(对 PageHelper 实现原理感兴趣的同学可以看下一篇文章…)
4.总结
MyBatis 插件调用流程:
插件相关的核心对象:
| 对象 | 作用 |
|---|---|
| Interceptor | 自定义插件需要实现接口,实现 3 个方法 |
| InterceptChain | 配置的插件解析后会保存在 Configuration 的 InterceptChain 中 |
| Plugin | 用来创建代理对象,包装四大对象 |
| Invocation | 对被代理类进行包装,可以调用 proceed()调用到被拦截的方法 |
插件应用场景:
| 作用 | 实现方式 |
|---|---|
| 水平分表 | 对 query update 方法进行拦截 在接口上添加注解,通过反射获取接口注解,根据注解上配置的参数进行分表,修改原 SQL,例 如 id 取模,按月分表 |
| 数据加解密 | update——加密;query——解密 获得入参和返回值 |
| 菜单权限控制 | 对 query 方法进行拦截 在方法上添加注解,根据权限配置,以及用户登录信息,在 SQL 加上权限过滤条件 |













还没有评论,来说两句吧...