天天看點

mybatis源碼學習(七)

mybatis加載plugins

上一篇講解了mybatis加載typeAliases的過程,mybatis加載别名的方法有3種,根據優先級,最優先的是注解,然後才是配置檔案,整個過程簡單的說,就是讀取相關的bean,然後儲存在TYPE_ALIASES集合裡面,供後面的使用。它主要的作用就是為類寫别名,使得mybatis在使用類的時候,可以直接使用别名,而不需要使用類名。

加載完typeAliases之後,接着就是加載plugins,在mybatis,plugins是非常有用的,它可以定義攔截器,對執行的sql語句進行攔截,是對mybatis的增強。

MyBatis 允許使用插件來攔截的方法調用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

在mybatis裡使用插件是非常簡單的,隻需實作 Interceptor 接口,并指定了想要攔截的方法簽名即可,下面舉例講解一下plugins的使用過程。

  1. 定義類,實作mybatis的Interceptor接口
@Intercepts(value = {@Signature(type= Executor.class,method = "query",args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})})
public class MyInterceptor implements Interceptor {

    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("----------- 執行開始。。。。。 ---------");
        // 調用方法,實際上就是攔截的方法
        Object result = invocation.proceed();
        System.out.println("----------- 執行結束。。。。。 ---------");
        return result;
    }

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

    public void setProperties(Properties properties) {

    }
}
           
  1. 在核心配置檔案裡,加上plugins配置
<plugins>
        <plugin interceptor="com.gzwx.plugins.interceptor.inceptor">
    </plugin>
           

上面攔截的是Executor裡面的query方法,其他的包括update、flushStatements等都不會攔截。同樣的,攔截其他例如ParameterHandler 、ResultSetHandler等方法是同樣的配置,隻是在簽名那裡需要修改一下。

再看加載plugins的源碼,通過調用pluginElement方法加載plugins插件,傳入的是核心配置檔案裡面的plugins節點資訊

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //周遊該節點,擷取interceptor節點屬性
        String interceptor = child.getStringAttribute("interceptor");
        //擷取節點下的其他屬性
        Properties properties = child.getChildrenAsProperties();
        //這裡是根據節點配置的類名去執行個體化類,在這裡面,首先會去找上一步加載的别名裡面的
        //TYPE_ALIASES集合,因為有的配置直接配置别名,而不是配置完整類名,如果在集合裡面找到
        //那麼直接傳回集合裡面的執行個體,否則,根據配置的類名去通過反射機制找到類,并執行個體化
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        //把插件類添加進configuration裡面,實際上就是加入到interceptorChain這個變量裡面,
        //interceptorChain變量的類型就是InterceptorChain,攔截器鍊,它裡面有一個list,這個方法就是把這個執行個體放到裡面的list裡
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
           

configuration的addInterceptor如下

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

InterceptorChain代碼如下:

public class InterceptorChain {

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

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}
           

到這裡,加載plugins就完成了,在這裡,僅僅是把插件執行個體化到configuration裡面。

繼續閱讀