mybatis源碼解析

  • 2019 年 12 月 31 日
  • 筆記

文章目錄

1. Configuration

2. MapperRegistry

3. MappedStatement

4. MapperProxyFactory

5. MapperProxy

6. MapperMethod

7. BoundSql

8. ResultHandler

9. TypeHandler

10. Mybatis四大關鍵類

10.1. Executor

10.2. ParameterHandler

10.3. ResultSetHandler

10.4. StatementHandler

11. 流程圖

11.1. Mybatis創建MapperProxy

11.2. Mybaits執行查詢

11.3. Mybaits執行查詢的簡化版

Configuration

  • mybatis的全局配置類,其中封裝了mybatis的全部配置,在mybaits載入全局配置文件和Mapper文件的時候,會將裡面的xml中的各個元素解析出來,封裝在其中。
  • 主要的解析流程在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration有詳細描述。
private void parseConfiguration(XNode root) {      try {        //issue #117 read properties first        propertiesElement(root.evalNode("properties"));        Properties settings = settingsAsProperties(root.evalNode("settings"));        loadCustomVfs(settings);        loadCustomLogImpl(settings);        typeAliasesElement(root.evalNode("typeAliases"));        pluginElement(root.evalNode("plugins"));        objectFactoryElement(root.evalNode("objectFactory"));        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));        reflectorFactoryElement(root.evalNode("reflectorFactory"));        settingsElement(settings);        // read it after objectFactory and objectWrapperFactory issue #631        environmentsElement(root.evalNode("environments"));        databaseIdProviderElement(root.evalNode("databaseIdProvider"));        typeHandlerElement(root.evalNode("typeHandlers"));        mapperElement(root.evalNode("mappers"));      } catch (Exception e) {        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);      }    }

MapperRegistry

  • 其中存放了所有的Mapper資訊,相當於Mapper的註冊中心
  • 所有的Mapper資訊都存放在Map<Class<?>, MapperProxyFactory<?>> knownMappers這個Map中,key是Mapper的全類名,value是MapperProxyFactory對象(用來創建代理對象)
  • 在mybaits載入配置文件解析Mapper的時候,會調用addMapper方法,將其添加到Map中。在獲取Mapper的時候會調用getMapper方法,利用MapperProxyFactory對象創建一個代理對象返回。

MappedStatement

  • 封裝了一個增刪改查標籤的全部屬性,一個標籤就是一個MappedStatement,保存在全局配置類中的Map<String, MappedStatement> mappedStatements

MapperProxyFactory

  • 創建Mapper代理對象的工廠類,其中最重要的兩個方法如下:
//使用MapperProxy創建一個代理對象  protected T newInstance(MapperProxy<T> mapperProxy) {      return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);    }      public T newInstance(SqlSession sqlSession) {        //封裝一個MapperProxy      final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);      return newInstance(mapperProxy);    }

MapperProxy

  • Mapper的代理類,實現了InvocationHandler,當mapper調用方法的時候真正執行的方法是invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {      try {        if (Object.class.equals(method.getDeclaringClass())) {          return method.invoke(this, args);        } else if (method.isDefault()) {          return invokeDefaultMethod(proxy, method, args);        }      } catch (Throwable t) {        throw ExceptionUtil.unwrapThrowable(t);      }      final MapperMethod mapperMethod = cachedMapperMethod(method);      return mapperMethod.execute(sqlSession, args);    }
  • 其中的屬性有sqlSession(其中的屬性有全局配置類Configuration)、mapperInterface(mapper真正的介面)、Map<Method, MapperMethod> methodCache

MapperMethod

  • 封裝了Mapper介面中的單個方法,其中有兩個重要的屬性,如下:
    • SqlCommand:封裝方法的全類名和執行的增刪改查的類型(SqlCommandType的枚舉類型)
    • MethodSignature:方法的簽名,其中封裝了該方法的一些資訊,比如返回類型,返回值的類型等資訊。
  • 其中有一個public Object execute(SqlSession sqlSession, Object[] args)方法,用來執行方法,如下:
    • 根據方法的不同類型,執行不同的邏輯
public Object execute(SqlSession sqlSession, Object[] args) {      Object result;      switch (command.getType()) {        case INSERT: {          Object param = method.convertArgsToSqlCommandParam(args);          result = rowCountResult(sqlSession.insert(command.getName(), param));          break;        }        case UPDATE: {          Object param = method.convertArgsToSqlCommandParam(args);          result = rowCountResult(sqlSession.update(command.getName(), param));          break;        }        case DELETE: {          Object param = method.convertArgsToSqlCommandParam(args);          result = rowCountResult(sqlSession.delete(command.getName(), param));          break;        }        case SELECT:          if (method.returnsVoid() && method.hasResultHandler()) {            executeWithResultHandler(sqlSession, args);            result = null;          } else if (method.returnsMany()) {            result = executeForMany(sqlSession, args);          } else if (method.returnsMap()) {            result = executeForMap(sqlSession, args);          } else if (method.returnsCursor()) {            result = executeForCursor(sqlSession, args);          } else {            Object param = method.convertArgsToSqlCommandParam(args);            result = sqlSession.selectOne(command.getName(), param);            if (method.returnsOptional()                && (result == null || !method.getReturnType().equals(result.getClass()))) {              result = Optional.ofNullable(result);            }          }          break;        case FLUSH:          result = sqlSession.flushStatements();          break;        default:          throw new BindingException("Unknown execution method for: " + command.getName());      }      if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {        throw new BindingException("Mapper method '" + command.getName()            + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");      }      return result;    }

BoundSql

  • 封裝了SQL語句的資訊,包括需要執行的sql語句,參數列表等資訊,通過MappedStatement中getBoundSql()方法創建。

ResultHandler

TypeHandler

Mybatis四大關鍵類

Executor

  • 執行器:在mybaits中負責執行增刪改查和事務的操作,mybatis會根據不同的executorType創建不同的執行器
  • Mybaits的執行器主要有如下的類型:
    • ExecutorType.SIMPLE:這個執行器類型不做特殊的事情。它為每個語句的執行創建一個新的預處理語句。
    • ExecutorType.REUSE:這個執行器類型會復用預處理語句。
    • ExecutorType.BATCH:這個執行器會批量執行所有更新語句,如果 SELECT 在它們中間執行,必要時請把它們區分開來以保證行為的易讀性。
  • 我們通過SqlSessionFactory創建sqlSession的時候可以傳入執行器的類型,如果不傳入,默認使用Simple類型的,在源碼中的創建的執行的邏輯如下:
//org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)    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;    }
  • 實現類型如下:
    • BatchExecutor:批量執行器
    • ReuseExecutor:復用執行器
    • SimpleExecutor:簡單執行器,默認的
    • CachingExecutor:快取執行器,mybaits中默認使用的執行器,用於二級快取

ParameterHandler

  • 主要作用就是設置為Statement設置參數

ResultSetHandler

  • 主要作用就是對最後的結果進行處理

StatementHandler

  • 顧名思義就是創建JDBC中的Statement,實現類如下:
    • SimpleStatementHandler
    • PreparedStatementHandler
    • CallableStatementHandler
  • 主要的作用就是處理Statement,執行查詢,對參數、查詢結果進行處理。
  • 主要的方法如下:
    • Statement prepare(Connection connection, Integer transactionTimeout):創建Statement
    • void parameterize(Statement statement:設置參數
    • void batch(Statement statement):批量處理
    • <E> List<E> query(Statement statement, ResultHandler resultHandler):執行查詢

流程圖

  • 分析源碼執行流程,畫出對應的流程圖,方便理解和記憶

Mybatis創建MapperProxy

  • 包括了讀取全局配置文件和Mapper文件生成全局配置類、創建Mapper代理對象的詳細過程

Mybaits執行查詢

  • 主要描述了mybaits通過代理對象如何執行查詢語句到對查詢結果的處理過程的詳細描述

Mybaits執行查詢的簡化版

  • 主要根據Mybatis中的重要的四大類,和SqlSession、TypeHandler進行簡化描述,如下: