mybatis的源碼應該算是比較容易閱讀的,首先mybatis核心功能就是執行Sql語句,但在其基礎上又有許多加強的地方(動態Sql,ORM等)。看一個框架的時候,第一步是對整個框架有一個大致的瞭解。例如mybatis,咱們能夠從初始化到完成一個sql請求爲主線,看一下涉及了哪些類。我我的總結了一下,mybatis的框架主要的核心類有4個 html
Configuration就是用於解析、保存、處理Mybatis的配置內容,包括了java
總結Configuration的功能,固然,如何讀取和解析相關文件是Configuration中大部分代碼作的事。這些都是爲了準備後面mybatis運行的基本條件。Configuration中建立類是由於建立的這些類都依賴於Configuration(但這樣作數據和邏輯沒有作到分離)。sql
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(); ... }
這些方法都是直接提供給外部調用的。看到這些方法是否是很親切。(我我的在看源碼的時候看到一些本身用過的一些類或方法的時候都有種莫名的親近感。感受終於和個人認知世界有交集了)緩存
SqlSessionFactor是用於建立SqlSession建造者,提供給外部快速建立一個SqlSession。是一個工廠類,而SqlSessionFactor的建立則是由SqlSessionFactorBuilder。 mybatis
前面說了SqlSession只是一個門面類,Executor纔是負責Sql語句執行的。所以Executor纔是整個mybatis核心。Executor的實現類有 架構
咱們看一個Executor參數最多的一個方法app
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
這些類都對執行Sql有必定關係框架
具體點來理解就是咱們定義的Sql映射語句,例如咱們xml定義的:ide
<select id="selectCountByPath" parameterType="java.lang.String" resultType="java.lang.Long"> select count(1) FROM config WHERE path = #{path} </select>
這個就是傳遞給sql映射的參數,用於生成和填充動態sql語句
限定一次查詢數據量,類很簡單,看代碼就明白,很少說
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; } }
這個和本地緩存有關,用於保存一個查詢語句的緩存對象,下次有相同的查詢語句的時候就會先嚐試從本地緩存中獲取。 注意:
一個查詢語句的在本地緩存中的key,根據sql語句,參數等等組成
這個對象就是本次實際須要執行的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; }
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()方法
要說直接和Jdbc打交道的就是各類Handler類,例如
上面的這些其實都不復雜,因此代碼仍是比較好理解的
每一個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等)。上面介紹了框架中的一些核心類,可是不少細節的地方值得咱們去深挖。這個就須要咱們能沉下來好好閱讀代碼。