Mybatis源碼解析

mybatis基本架構

mybatis的源碼應該算是比較容易閱讀的,首先mybatis核心功能就是執行Sql語句,但在其基礎上又有許多加強的地方(動態Sql,ORM等)。看一個框架的時候,第一步是對整個框架有一個大致的瞭解。例如mybatis,咱們能夠從初始化到完成一個sql請求爲主線,看一下涉及了哪些類。我我的總結了一下,mybatis的框架主要的核心類有4個 輸入圖片說明html

Configuration

Configuration就是用於解析、保存、處理Mybatis的配置內容,包括了java

  • mybatis基本配置,例如支持數據庫中的字段支持下標轉駝峯mapUnderscoreToCamelCase=true等等,參看Mybatis配置說明
  • SqlMapper管理,也就是經過xml或者註解寫的一些sql映射。相關的類能夠查看源碼中MappedStatement類。
  • 建立類,Configuration還有一些建立類的功能,例如Executor、StatementHandler。這個2個類後面還會說到

小節Configuration

總結Configuration的功能,固然,如何讀取和解析相關文件是Configuration中大部分代碼作的事。這些都是爲了準備後面mybatis運行的基本條件。Configuration中建立類是由於建立的這些類都依賴於Configuration(但這樣作數據和邏輯沒有作到分離)。sql

SqlSession

SqlSession多是mybatis中咱們最經常使用的類,其實他是一個門面類,直接對外提供服務數據庫

public interface SqlSession extends Closeable {
    <T> T selectOne(String statement);
    <E> List<E> selectList(String statement, Object parameter);
    int delete(String statement);
    void rollback();
    void commit();
    ...

}

這些方法都是直接提供給外部調用的。看到這些方法是否是很親切。(我我的在看源碼的時候看到一些本身用過的一些類或方法的時候都有種莫名的親近感。感受終於和個人認知世界有交集了)緩存

SqlSession的建立

SqlSessionFactor是用於建立SqlSession建造者,提供給外部快速建立一個SqlSession。是一個工廠類,而SqlSessionFactor的建立則是由SqlSessionFactorBuilder。 輸入圖片說明mybatis

Executor

前面說了SqlSession只是一個門面類,Executor纔是負責Sql語句執行的。所以Executor纔是整個mybatis核心。Executor的實現類有 輸入圖片說明架構

  • BaseExecutor:看名字知道是最基礎Executor,其餘的Executor都和這個類有必定的關係
  • CachingExecutor:每次查詢的時候會先從緩存中獲取,每次有增刪改的時候會讓緩存失效。CachingExecutor實際上是一個代理內,內部代理了BaseExecutor(或其子類)。在BaseExecutor基礎上增長了緩存操做。

相關類

咱們看一個Executor參數最多的一個方法app

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

這些類都對執行Sql有必定關係框架

MappedStatement

具體點來理解就是咱們定義的Sql映射語句,例如咱們xml定義的:ide

<select id="selectCountByPath" parameterType="java.lang.String" resultType="java.lang.Long">
    select count(1) FROM config
    WHERE path = #{path}
</select>

paramter

這個就是傳遞給sql映射的參數,用於生成和填充動態sql語句

RowBound

限定一次查詢數據量,類很簡單,看代碼就明白,很少說

public class RowBounds {

  public static final int NO_ROW_OFFSET = 0;
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
  public static final RowBounds DEFAULT = new RowBounds();

  private int offset;
  private int limit;

  public RowBounds() {
    this.offset = NO_ROW_OFFSET;
    this.limit = NO_ROW_LIMIT;
  }

  public RowBounds(int offset, int limit) {
    this.offset = offset;
    this.limit = limit;
  }
}

ResultHandler

這個和本地緩存有關,用於保存一個查詢語句的緩存對象,下次有相同的查詢語句的時候就會先嚐試從本地緩存中獲取。 注意:

  • mybatis有2級緩存,第一級是CachingExecutor,第二級緩存就是mybatis的本地緩存,也就是和ResultHandler
  • 緩存失效策略是和一級緩存同樣,任何增刪改都會清空本地緩存

CacheKey

一個查詢語句的在本地緩存中的key,根據sql語句,參數等等組成

BoundSql

這個對象就是本次實際須要執行的sql語句有關的信息,

public class BoundSql {

  private String sql;
  private List<ParameterMapping> parameterMappings;
  private Object parameterObject;
  private Map<String, Object> additionalParameters;
  private MetaObject metaParameters;
  ...

若是說parameter參數是實際傳入的參數,那麼BoundSql就是根據傳入參數進行相關解析後的結果。他的建立在MappedStatement中,根據parameter和當前執行MappedStatement生成

public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }
    
    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }
    
    return boundSql;
}

Interceptor

Mybatis提供了Interceptor用於在執行Executor以前進行一些操做,mybatis是怎麼使用Interceptor。其實就是在建立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;
 }

這裏主要是經過jdk動態代理實現的

public class Plugin implements InvocationHandler {
  ...
  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;
  }
  
  ...
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      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);
    }
  }

這樣在調用Executor的時候就會先判斷是否知足Interceptor的執行條件,知足則會先執行Intercepter#intercept()方法

最底層的Handler

要說直接和Jdbc打交道的就是各類Handler類,例如

  • StatementHandler: 處理java.sql.Statement
  • ParameterHandler: 向PreparedStatement中設置參數
  • ResultSetHandler:處理sql執行結果,並轉換成指定的類對象

上面的這些其實都不復雜,因此代碼仍是比較好理解的

Transaction

每一個Executor生成的時候都會把Transaction傳入,在BaseExecutor中Transaction是其成員變量,那Transaction的做用是什麼呢?

public interface Transaction {

  Connection getConnection() throws SQLException;

  void commit() throws SQLException;

  void rollback() throws SQLException;

  void close() throws SQLException;

  Integer getTimeout() throws SQLException;
  
}

其實以前一直都沒提到過Connect誰來管理,這裏能夠看出來,Transaction負責了Connection的獲取,以及對此次Connect的提交和回滾等操做。這個類也是比較好理解的。Executor的commit或者rollback最後都是調用Transaction的

總結

能夠看出,mybatis的源碼是比較容易閱讀的(相對於Spring等)。上面介紹了框架中的一些核心類,可是不少細節的地方值得咱們去深挖。這個就須要咱們能沉下來好好閱讀代碼。

相關文章
相關標籤/搜索