MyBatis的插件机制源代码解析


MyBatis的插件机制源代码解析

MyBatis插件机制主要从插件的执行流程来进行分析,分别是查找/注册阶段,包装阶段,执行阶段扩展点来分析

查找/注册MyBatis插件阶段

  • org.apache.ibatis.session.Configuration#addInterceptor

是使用Configuration#addInterceptor将插件注册到InterceptorChain拦截器链上;

有两个种使用的方法:

  1. org.apache.ibatis.builder.xml.XMLConfigBuilder#pluginElement
    通过解析XML配置文件中的标签,获取到插件类名,在通过类名加载的方式

  2. 直接外部调用的方式,常见的MyBatis-plus就是通过这种方式来注册插件的
    MybatisSqlSessionFactoryBean.buildSqlSessionFactory()

在通过xml配置文件进行加载时,会根据自定义的类加载器 -> 默认类加载器 -> 当前线程的类加载器 -> ClassLoaderWrapper.getClassLoader() -> 系统类加载器的顺序来尝试进行加载,这里比较特别;

包装阶段

将Interceptor注册到InterceptorChain拦截器链上之后,下一步就是将InterceptorChain与Executor进行关联;

  • DefaultSqlSessionFactory_openSessionFromDataSource

DefaultSqlSessionFactory_openSessionFromDataSource.png

我们以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注解来对插件类进行描述,描述插件的作用域(类/方法)

  • 执行过程

  1. pluginAll
    Configuration中创建Handler后,立即执行pluginAll方法,可以看到分别为ResultSetHandler,ParameterHandler,StatementHandler,Executor四种

  2. 当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 返回的结果, 根据定义返回类型进行封装返回

参考资料

mybatis 四大核心组件
Mybatis3详解(十九)----SqlSession下的四大对象


  TOC