Mybatis源码解析-4.插件配置解析

Mybatis插件配置解析

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

插件的配置通过<plugins>标签完成,Mybatis文档中给出的例子如下:

<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

XMLConfigBuilder的配置解析逻辑中,pluginElement(XNode parent)方法用于解析插件配置。代码如下:

private void parseConfiguration(XNode root) {
    ...
    pluginElement(root.evalNode("plugins"));
    ...

}

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      // 遍历plugins标签的所有plugin子标签
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        // 将所有的plugin标签中声明的所有property转化为Properties对象
        Properties properties = child.getChildrenAsProperties();
        // 实例化配置的plugin
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        // 将plugin需要的属性初始化到plugin中
        interceptorInstance.setProperties(properties);
        // 将初始化完成的plugin放入到Configuration对象的interceptorChain属性中
        configuration.addInterceptor(interceptorInstance);
      }
    }
}

可以看到,插件配置被解析时就已经创建了插件的实例对象,而每个插件是按照拦截器模式进行处理业务的,我们这里可以再次考察两个问题:

  1. 插件的保存顺序如何?
  2. 拦截器接口到底是怎样的格式?

插件的保存顺序

要考察插件的保存顺序,我们必须深入到如下代码中:

configuration.addInterceptor(interceptorInstance);

考察Configuration对象的addInterceptor(Interceptor)方法,可以发现:

public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}

该方法仅仅为拦截器链添加了一个拦截器罢了,具体的拦截器添加逻辑在InterceptorChain中,那么拦截器链到底是怎样的结构呢?其实拦截器链就是一个链表:

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

}

其添加逻辑更是简单,完全不用考虑拦截器的执行顺序,仅仅是将链表中添加一个节点罢了。

public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
}

综上所述,我们在Mybatis配置文件中配置的所有插件,在配置解析完毕后,会变成一个拦截器,并存储于Configuration对象的拦截器链中,大致逻辑如下图所示:

【mybatis-config.xml - <plugins>|                       ^
    v                       |
【XMLConfigBuilder.parseConfiguration(parser.evalNode("/configuration")】  // 解析配置文件
    |                   ^
    v                   |
【XMLConfigBuilder.pluginElement(root.evalNode("plugins"))】  // 解析plugins标签
        |           ^
        v           |
【XMLConfigBuilder.configuration.addInterceptor(interceptorInstance)】// 创建拦截器,并将拦截器放入到configuration对象的拦截器链中
            |   ^
            v   |
【interceptorChain.addInterceptor(interceptor);

拦截器接口格式

通过上一部分我们知道,Mybatis的插件最后都会被作拦截器处理,这里我们考察一下在Mybatis中拦截器接口的格式:

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  default void setProperties(Properties properties) {
    // NOP
  }

}

该拦截器接口共声明了三个方法:

  1. setProperties(Properties properties):通过上一节分析我们知道,该方法是在解析plugin标签的最后一步,将plugin标签的子标签中声明的所有properties设置到拦截器实例中。
  2. plugin(Object target):该方法用于声明通过何种方式实现拦截器模式,Mybatis默认情况下是使用JDK动态代理的方式实现的拦截器模式,你可以覆盖该方法对其进行更改,之后介绍到拦截器具体执行步骤时,会对比拦截器模式这样实现的好处与缺点
  3. intercept(Invocation):该方法是拦截器执行的真正业务逻辑。

如此看来,Mybatis的拦截器接口声明的三个方法分别用于:初始化、构建以及真正的业务操作。将三者解耦开来,是一种蛮不错设计。

至此,我们已经分析完,Mybatis插件初始化过程,至于更多关于插件执行的细节,我们会在后面进行更深入的分析。不过现在先让我们把Mybatis的配置解析分析完,接下来让我们去看一下最重要的<mappers>标签的解析。

下一节:Mybatis源码解析-5.Mappers标签解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值