插件机制
一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展。这样的
好处是显而易见的,一是增加了框架的灵活性。二是开发者可以结合实际需求,对框架进行
拓展,使其能够更好的工作。以 MyBatis 为例,我们可基于 MyBatis 插件机制实现分页、
分表,监控等功能。由于插件和业务无关,业务也无法感知插件的存在。因此可以无感植入
插件,在无形中增强功能。
开发 MyBatis 插件需要对 MyBatis 比较深了解才行,一般来说最好能够掌握 MyBatis
的源码,门槛相对较高。本篇文章在分析完 MyBatis 插件机制后,会手写一个简单的分页
插件,以帮助大家更好的掌握 MyBatis 插件的编写。
1、插件机制原理
我们在编写插件时,除了需要让插件类实现 Interceptor 接口外,还需要通过注解标注
该插件的拦截点。所谓拦截点指的是插件所能拦截的方法,MyBatis 所允许拦截的方法如
下:
- Executor: update, query, flushStatements, commit, rollback,
getTransaction, close, isClosed - ParameterHandler: getParameterObject, setParameters
- ResultSetHandler: handleResultSets, handleOutputParameters
- StatementHandler: prepare, parameterize, batch, update, query
如果我们想要拦截 Executor 的 query 方法,那么可以这样定义插件。
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args ={
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class}
)
})
public class ExamplePlugin implements Interceptor {
// 省略逻辑
}
除此之外,我们还需将插件配置到相关文件中。这样 MyBatis 在启动时可以加载插件,
并保存插件实例到相关对象(InterceptorChain,拦截器链)中。待准备工作做完后,MyBatis
处于就绪状态。我们在执行 SQL 时,需要先通过 DefaultSqlSessionFactory 创 建
SqlSession 。Executor 实例会在创建 SqlSession 的过程中被创建,Executor 实例创建完毕
后,MyBatis 会通过 JDK 动态代理为实例生成代理类。这样,插件逻辑即可在 Executor 相
关方法被调用前执行。以上就是 MyBatis 插件机制的基本原理。接下来,我们来看一下原
理背后对应的源码是怎样的。
1.1 植⼊插件逻辑
本节,我将以 Executor 为例,分析 MyBatis 是如何为 Executor 实例植入插件逻辑的。
Executor 实例是在开启 SqlSession 时被创建的,因此,下面我们从源头进行分析。先来看
一下 SqlSession 开启的过程。
// -☆- DefaultSqlSessionFactory
public SqlSession openSession() {
return openSessionFromDataSource(
configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 省略部分逻辑
// 创建 Executor
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
}
catch (Exception e) {
...}
finally {
...}
}
Executor 的创建过程封装在 Configuration 中,我们跟进去看看看。
// -☆- Configuration
public Executor newExecutor(Transaction transaction,
ExecutorType executorType) {
executorType = executorType == null ?
defaultExecutorType : executorType;
executorType = executorType == null ?
ExecutorType.SIMPLE : executorType;
Executor executor;
// 根据 executorType 创建相应的 Executor 实例
if (ExecutorType.BATCH == executorType) {
...}
else if (ExecutorType.REUSE