
该系列文章针对 Mybatis 3.5.1 版本
一、Mybatis 插件的作用
Mybatis 针对 SQL 映射语句执行过程中进行拦截处理,而对应的拦截器 Mybaits 又称之为 插件(这些插件就是Mybatis的扩展点)
在 Mybaits 中允许用插件来拦截的方法包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
通过插件的方式可以实现 SQL打印、分页等插件功能实现。
二、Mybatis 插件配置
插件代码逻辑实现后还需要加载到 Mybatis 中才能生效,Mybatis 提供了 <plugin>
配置标签,用来声明。
插件在 mybatis-config.xml
中的配置案例,如下:

通过 <plugin>
标签,然后在指定的属性 <plugin interceptor=class全路径>
interceptor
配置插件实现类的全路径即可。
三、`<plugins>` 标签解析
再来回顾一下,XMLConfigBuilder
解析时序简图,如下:

在时序图中<plugins>
加载解析在XMLConfigBuilder#pluginElement
中完成,相关解析代码如下:

如上述代码,<plugins>
的解析流程很简单
1、加载 <plugins>
下的子标签 <plugin>
2、获取 <plugin interceptor=class全路径名>
中 interceptor 属性中的 class 全路径名
3、class 必须实现了 Interceptor
接口,如果满足,通过反射实例化类
4、把类加载到存放拦截器的拦截器容器,拦截器链 InterceptorChain
简单来看一下 InterceptorChain
代码定义

如上述代码所示,InterceptorChain
本身就是一个容器,用来存放所有从 <plugins>
读取到的拦截器对象。
而这里的拦截器列表,在使用过程中,会通过代理的方式,对目标对象层层代理,通过责任链的方式实现代码执行前后的层层过滤,相关逻辑图如下:

四、interceptor 过滤链代理处理代码
上面提到了 interceptor 过滤链的实现是通过代理的方式层层包裹实现的,下面来简单阅读代理流程源码
Mybatis 中只针对 Executor
、ParameterHandler
、ResultSetHandler
、StatementHandler
,这四种情况追加了过滤连的处理。
相关的处理方法入口为 InterceptorChain#pluginAll
,如下图

如上述代码,遍历所有的插件,调用插件本身的plugin
方法来处理,也就是Interceptor#plugin
,来看通用实现
(也可以自定义实现逻辑),代码如下

如上述代码,通用的代理逻辑交由工具类 Plugin
来实现,
接着来看一下 Plugin#wrap
方法源码

如上述源码所示,通过配对当前 interceptor 是否符合目标对象 target ,如果配对,构建相应的代理对象。
以此类推,随后实现如下图的效果:

五、总结
通过上述源码解析能够知道一个插件,也就是一个 Interceptor
的定义需要满足两个条件
1、该插件实现类实现了 Interceptor
接口
2、该插件实现类通过注解 @Intercepts
指定了该插件需要拦截的对象,也就是Executor
、ParameterHandler
、ResultSetHandler
、StatementHandler
中的一种或者多种
<plugins>
标签中配置的是一系列拦截器,这些拦截器通过代理的方式组合起来实现了过滤器链。
而这些过滤器数据存储在 InterceptorChain
中,最终数据仍然会存在 Configuration
中,
相关的 Configuration
逻辑图如下:
