Mybatis支持哪些插件類型?面試中80%人答不全

分享我最近整理的Mybatis知識點,簡單介紹了Mybatis插件功能的使用和實現方式~sql

![Mybatis中的插件問題,看這篇就夠了(附源碼分析)](https://p1-tt.byteimg.com/origin/pgc-image/4018034ce3474fce9ae92dc86fce8095?from=pc)

Mybatis支持哪些類型插件?

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

如何使用插件

  • 自定義插件實現org.apache.ibatis.plugin.Interceptor接口apache

  • 經過 @Intercepts註解代表插件加強的類型,方法markdown

    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})public class SQLStatsInterceptor implements Interceptor { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); System.out.println("mybatis intercept sql:" + sql); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { String dialect = properties.getProperty("dialect"); System.out.println("mybatis intercept dialect:"+dialect); }}mybatis

  • config.xml中配置插件app

爲何只支持上述的四種類型的插件呢?

只是由於在Configuration中定義了以下幾個方法:ide

/**   *plugin在此對ParameterHandler進行加強   */  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);    return parameterHandler;  }  /**   *plugin在此對ResultSetHandler進行加強   */  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,      ResultHandler resultHandler, BoundSql boundSql) {    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);    return resultSetHandler;  }  /**   *plugin在此對StatementHandler進行加強   */  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);    return statementHandler;  }  public Executor newExecutor(Transaction transaction) {    return newExecutor(transaction, defaultExecutorType);  }  /**   *plugin在此對Executor進行加強   */  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;    if (ExecutorType.BATCH == executorType) {      executor = new BatchExecutor(this, transaction);    } else if (ExecutorType.REUSE == executorType) {      executor = new ReuseExecutor(this, transaction);    } else {      executor = new SimpleExecutor(this, transaction);    }    if (cacheEnabled) {      executor = new CachingExecutor(executor);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }
複製代碼

加強實現的源碼 在解析config.xml文件的時候會將中配置的插件會添加到Configuration中到interceptorChain中源碼分析

private void pluginElement(XNode parent) throws Exception {    //若是存在plugings節點    if (parent != null) {      //遍歷全部子節點      for (XNode child : parent.getChildren()) {        //獲取interceptor屬性        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        //建立Interceptor實例        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();        //設置屬性        interceptorInstance.setProperties(properties);        //向攔截器鏈注入攔截器        configuration.addInterceptor(interceptorInstance);      }    }  }

public void addInterceptor(Interceptor interceptor) {    interceptorChain.addInterceptor(interceptor);  }
複製代碼

SimpleExecutor中執行doUpdate方法是會經過configuration去建立一個StatementHandlerthis

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {    Statement stmt = null;    try {      log.trace(" SimpleExecutor ms.getConfiguration()");      Configuration configuration = ms.getConfiguration();      log.trace("SimpleExecutor configuration.newStatementHandler");      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.update(stmt);    } finally {      closeStatement(stmt);    
複製代碼

上文已經列出來了,在newStatementHandler中會對StatementHandler進行加強spa

/**   *plugin在此對StatementHandler進行加強   */  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);    return statementHandler;  }

/** * 攔截器鏈 * @author Clinton Begin */public class InterceptorChain {    //攔截器列表    private final List interceptors = new ArrayList();    //對攔截器進行加強    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 getInterceptors() {        return Collections.unmodifiableList(interceptors);    }}

@Override    public Object plugin(Object target) {        return Plugin.wrap(target, this);    }
複製代碼

經過上文中發現彷佛全部攔截器都進行了加強處理,事實上不是這樣都,具體使用哪些攔截器對接口進行加強是以下方法中處理的。同時能夠發現 Plugin實現了InvocationHandler接口,也就是調用處理也是在該類中完成的,下面主要就是Plugin中的實現源碼插件

//對目標對象使用過interceptor進行加強  public static Object wrap(Object target, Interceptor interceptor) {    //獲取攔截類,方法映射表    Map, Set> 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;  }

 private static Map, Set> getSignatureMap(Interceptor interceptor) {    //獲取類上@Intercepts的註解    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);    // 若是插件上沒有@Intercepts註解,拋出異常    if (interceptsAnnotation == null) {      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());          }    //@Intercepts註解中全部簽名    Signature[] sigs = interceptsAnnotation.value();    Map, Set> signatureMap = new HashMap, Set>();    //遍歷全部簽名    for (Signature sig : sigs) {      //根據類型從簽名映射表中獲取方法集合      Set methods = signatureMap.get(sig.type());      if (methods == null) {        //若是方法集合爲null,則建立一個空集合並放入到映射表中        methods = new HashSet();        signatureMap.put(sig.type(), methods);      }      try {        //根據方法名稱,參數類型列表從指定的class中獲取方法        Method method = sig.type().getMethod(sig.method(), sig.args());        //若是找到指定的方法則添加到集合中        methods.add(method);      } catch (NoSuchMethodException e) {        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);      }    }    return signatureMap;  }
複製代碼

此次能夠發現經過JDK的動態代理進行了加強,在進行方法調用對時候會執行一下方法,在該方法中判斷當前方法是否須要加強,若是須要就會調用interceptor 進行加強處理

@Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    try {      Set 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);    }  }
複製代碼
來源:網易工程師--張偉

有任何問題歡迎留言交流~

看到這裏的小夥伴,若是你喜歡這篇文章的話,別忘了轉發、收藏、留言互動

若是對文章有任何問題,歡迎在留言區和我交流~

最近我新整理了一些Java資料,包含面經分享、模擬試題、和視頻乾貨,若是你須要的話,歡迎留言or私信我

還有,關注我!關注我!關注我!

相關文章
相關標籤/搜索