MyBatis的插件机制源代码解析
MyBatis插件机制主要从插件的执行流程来进行分析,分别是查找/注册阶段,包装阶段,执行阶段和扩展点来分析
查找/注册MyBatis插件阶段
- org.apache.ibatis.session.Configuration#addInterceptor
是使用Configuration#addInterceptor将插件注册到InterceptorChain拦截器链上;
有两个种使用的方法:
-
org.apache.ibatis.builder.xml.XMLConfigBuilder#pluginElement
通过解析XML配置文件中的标签,获取到插件类名,在通过类名加载的方式 -
直接外部调用的方式,常见的MyBatis-plus就是通过这种方式来注册插件的
MybatisSqlSessionFactoryBean.buildSqlSessionFactory()
在通过xml配置文件进行加载时,会根据自定义的类加载器 -> 默认类加载器 -> 当前线程的类加载器 -> ClassLoaderWrapper.getClassLoader() -> 系统类加载器的顺序来尝试进行加载,这里比较特别;
包装阶段
将Interceptor注册到InterceptorChain拦截器链上之后,下一步就是将InterceptorChain与Executor进行关联;
- DefaultSqlSessionFactory_openSessionFromDataSource
我们以openSessionFromDataSource方法为例,在创建Executor的过程中会向Plugin.wrap(Executor);
下面我们详细分析Plugin.wrap()方法
public class Plugin implements InvocationHandler {
//wrap方法
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,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
//invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//signatureMap是目标执行器需要执行的插件方法
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
Plugin实现InvocationHandler接口,重写invoke方法,invoke方法在targer对象执行方法时都会先进入invoke方法体,这样就为插件的执行提供了底层基础;
执行阶段
-
声明插件
插件在声明时主要是使用@Intercepts和@Signature注解来对插件类进行描述,描述插件的作用域(类/方法) -
执行过程
-
pluginAll
在Configuration中创建Handler后,立即执行pluginAll方法,可以看到分别为ResultSetHandler,ParameterHandler,StatementHandler,Executor四种 -
当Target class在执行时会先执行Plugin.invoke方法,就会执行Interceptor.intercept中的实现方法
扩展点
在上面的执行步骤中可以看到ResultSetHandler,ParameterHandler,StatementHandler,Executor执行pluginAll方法;
-
Executor
一个 SqlSession 对应一个 Executor 对象,Executor对象负责增删改查的具体操作; -
ParameterHandler
mybatis 提供的参数处理器, 没有过多的类关联关系, 只有一个默认的实现类; -
StatementHandler
StatementHandler是mybatis创建Statement的处理器, 会负责Statement的创建工作,主要是SimpleStatementHandler,PrepareStatementHandler,CallableStatementHandler组成;主要是进行适配JDBC的Statement创建和执行的工作 -
ResultSetHandler
ResultSetHandler 作用域只有一个, 那就是负责处理 Statement 返回的结果, 根据定义返回类型进行封装返回