這篇文章我們來深入閱讀下Mybatis的源碼,希望以後可以對底層框架不那麼畏懼,學習框架設計中好的思想;
架構原理
架構圖
架構流程圖
上面這兩幅圖來源於網絡,不過畫的很好,基本說明了Mybatis的架構流程。
說明:
-
Mybatis配置文件
- SqlMapConfig.xml,此文件作爲mybatis的全局配置文件,配置了mybatis的運行環境等信息。
- Mapper.xml,此文件作爲mybatis的sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在 SqlMapConfig.xml中加載。
-
SqlSessionFactory
- 通過mybatis環境等配置信息構造SqlSessionFactory,即會話工廠。
-
SqlSession
- 通過會話工廠創建sqlSession即會話,程序員通過sqlsession會話接口對數據庫進行增刪改查操作。
-
Executor執行器
- mybatis底層自定義了Executor執行器接口來具體操作數據庫,Executor接口有兩個實現,一個是基本執行器(默認)、一個是緩存執行器,sqlsession底層是通過executor接口操作數據庫的。
-
MappedStatement
- 它也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個selectinsertupdatedelete標籤對應一個Mapped Statement對象,selectinsertupdatedelete標籤的id即是Mapped statement的id。
- Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過MappedStatement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
- Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過MappedStatement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。
調用流程圖
Executor
MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護
StatementHandler
封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數、將Statement結果集轉換成List集合。
ParameterHandler
負責對用戶傳遞的參數轉換成JDBC Statement 所需要的參數
ResultSetHandler
負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合
TypeHandler
負責java數據類型和jdbc數據類型之間的映射和轉換
SqlSource
負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,並返回BoundSql表示動態生成的SQL語句以及相應的參數信息
源碼解析
加載全局配置文件
- 找入口:SqlSessionFactoryBuilder#build方法
SqlSessionFactoryBuilder#build 構建SqlSessionFactory
XMLConfigBuilder#parse 全局配置文件解析,封裝成Configuration對象
#parseConfiguration 從根路徑開始解析,加載的信息設置到Configuration對象中
#mapperElement 解析mapper映射文件
XMLMapperBuilder#parse 具體解析mapper映射文件
SqlSessionFactoryBuilder#build:創建SqlSessionFactory接口的默認實現類
- 總結
1.SqlSessionFactoryBuilder創建SqlsessionFactory時,需要傳入一個Configuration對象。
2.XMLConfigBuilder對象會去實例化Configuration。
3.XMLConfigBuilder對象會去初始化Configuration對象。
通過XPathParser去解析全局配置文件,形成Document對象
通過XPathParser去獲取指定節點的XNode對象。
解析Xnode對象的信息,然後封裝到Configuration對象中
- 相關類和接口
|--SqlSessionFactoryBuilder
|--XMLConfigBuilder
|--XPathParser
|--Configuration
加載映射配置文件
- 找入口:XMLConfigBuilder#mapperElement方法
XMLConfigBuilder#mapperElement:解析全局配置文件中的<mappers>標籤
|--XMLMapperBuilder#構造方法:專門用來解析映射文件的
|--XPathParser#構造方法:
|--XPathParser#createDocument():創建Mapper映射文件對應的Document對象
|--MapperBuilderAssistant#構造方法:用於構建MappedStatement對象的
|--XMLMapperBuilder#parse():
|--XMLMapperBuilder#configurationElement:專門用來解析mapper映射文件
|--XMLMapperBuilder#buildStatementFromContext:用來創建MappedStatement對象的
|--XMLMapperBuilder#buildStatementFromContext
|--XMLStatementBuilder#構造方法:專門用來解析MappedStatement
|--XMLStatementBuilder#parseStatementNode:
|--MapperBuilderAssistant#addMappedStatement:創建 MappedStatement對象
|--MappedStatement.Builder#構造方法
|--MappedStatement.Builder#build方法:創建MappedStatement對象,並存儲 到Configuration對象中
- 相關接口和類
|--XMLConfigBuilder
|--XMLMapperBuilder
|--XPathParser
|--MapperBuilderAssistant
|--XMLStatementBuilder
|--MappedStatement
SqlSource創建流程
- 找入口:XMLLanguageDriver#createSqlSource
XMLLanguageDriver#createSqlSource 創建SqlSource,解析SQL,封裝SQL語句(出參數綁定)和入參信息
XMLScriptBuilder 構造函數:初始化動態SQL中的節點處理器集合
XMLScriptBuilder#parseScriptNode
#parseDynamicTags 解析select\insert\ update\delete標籤中的SQL語句,最終將解析到的SqlNode封裝到MixedSqlNode中的List集合中
DynamicSqlSource 構造方法:如果SQL中包含${}和動態SQL語句,則將SqlNode封裝到DynamicSqlSource
RawSqlSource 構造方法:如果SQL中包含#{},則將SqlNode封裝到RawSqlSource中,並指定parameterType
SqlSourceBuilder#parse
ParameterMappingTokenHandler 構造方法
GenericTokenParser#構造方法,指定待分析的openToken和closeToken並指定處理器
GenericTokenParser#parse 解析#{}
ParameterMappingTokenHandler#handleToken 處理token(#{}/${})
#buildParameterMapping 創建ParameterMapping對象
StaticSqlSource 構造方法,將解析之後的sql信息,封裝到StaticSqlSource 對象
- 相關類和接口
|--XMLLanguageDriver
|--XMLScriptBuilder
|--SqlSource
|--SqlSourceBuilder
創建Mapper代理對象
- 找入口:DefaultSqlSession#getMapper
|--DefaultSqlSession#getMapper:獲取Mapper代理對象
|--Configuration#getMapper:獲取Mapper代理對象
|--MapperRegistry#getMapper:通過代理對象工廠,獲取代理對象
|--MapperProxyFactory#newInstance:調用JDK的動態代理方式,創建Mapper代理
SqlSession執行主流程
- 找入口:DefaultSqlSession#selectList()
DefaultSqlSession#selectList
CachingExecutor#query
BaseExecutor#query 委託給BaseExecutor執行
#queryFromDatabase
SimpleExecutor#doQuery 執行查詢
Configuration#newStatementHandler 創建路由功能的StatementHandler,根據MappedStatement中的StatementType
SimpleExecutor#prepareStatement 設置PreapreStatement 的參數
BaseExecutor#getConnection 獲取數據庫連接
BaseStatementHandler#prepare 創建Statement PrepareStatement、Statement、CallableStatement)
PreparedStatementHandler#parameterize 設置參數
PreparedStatementHandler#query 執行SQL語句(已經設置過參數),並且映射結果集
com.mysql.jdbc.PreparedStatement#execute 調用JDBC的api執行Statement
DefaultResultSetHandler#handleResultSets 處理結果集
- 相關接口和類
|--DefaultSqlSession
|--Executor
|--CachingExecutor
|--BaseExecutor
|--SimpleExecutor
|--StatementHandler
|--RoutingStatementHandler
|--PreparedStatementHandler
|--ResultSetHandler
|--DefaultResultSetHandler
BoundSql獲取流程
- 找入口:MappedStatement#getBoundSql方法
MappedStatement#getBoundSql
DynamicSqlSource#getBoundSql
SqlSourceBuilder#parse 執行解析:將帶有#{}的SQL語句進行解析,然後封裝到StaticSqlSource中
GenericTokenParser #構造方法,指定待分析的openToken和closeToken,並指定處理器
GenericTokenParser#parse 解析SQL語句,處理openToken和closeToken中的內容
ParameterMappingTokenHandler#handleToken 處理token(#{}/${})
#buildParameterMapping 創建 ParameterMapping對象
StaticSqlSource#構造方法 : 將解析之後的SQL信息,封裝到StaticSqlSource
|--RawSqlSource#getBoundSql
|--StaticSqlSource#getBoundSql
|--BoundSql#構造方法:將解析後的sql信息、參數映射信息、入參對象組合到BoundSql對象中
參數映射流程
- 找入口: 其實就是SqlSession執行流程中的 PreparedStatementHandler#parameterize
|--PreparedStatementHandler#parameterize:設置PreparedStatement的參數
|--DefaultParameterHandler#setParameters:設置參數
|--BaseTypeHandler#setParameter:
|--xxxTypeHandler#setNonNullParameter:調用PreparedStatement的setxxx方法
處理結果集
- 找入口:其實就是SqlSession執行流程中的 DefaultResultSetHandler#handleResultSets
|--DefaultResultSetHandler#handleResultSets
|--DefaultResultSetHandler#handleResultSet
|--DefaultResultSetHandler#handleRowValues
|--DefaultResultSetHandler#handleRowValuesForSimpleResultMap
|--DefaultResultSetHandler#getRowValue
|--DefaultResultSetHandler#createResultObject:創建映射結果對象
|--DefaultResultSetHandler#applyAutomaticMappings
|--DefaultResultSetHandler#applyPropertyMappings
基本上Mybatis的流程就是這樣了,其中還有很多實現細節暫時看不太懂。 我認爲學習框架源碼分爲兩步:
- 抓住主線,掌握框架的原理和流程;
- 理解了處理思路之後,再去理解面向對象思想和設計模式的用法;
目前第一步尚有問題,需要多走幾遍源碼,加深下理解,一起加油~~