本章經過一個簡單的例子,來了解 MyBatis 執行一條 SQL 語句的大體過程是怎樣的。java
案例代碼以下所示:sql
public class MybatisTest { @Test public void selectByPrimaryKey() throws IOException { // 3 StudentDao studentDao = getSqlSession().getMapper(StudentDao.class); // 4 Student student = studentDao.selectByPrimaryKey(1L); System.out.println(student); } /** * 獲取SqlSession * * @return */ private SqlSession getSqlSession() throws IOException { // 1 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); // 2 return sqlSessionFactory.openSession(true); } }
Configuration
第一步,經過資源加載模塊加載配置文件,解析器模塊解析 XML 文件,生成 Configuration 對象。數據庫
源碼內容參考:org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) 方法apache
深刻 parse() 方法能夠查看配置文件的具體解析過程,以及如何生成 Configuration 對象。緩存
Configuration 對象中緩存了 mybatis-config.xml 配置文件以及映射配置文件的全部內容。session
SqlSession
第二步,經過 SqlSessionFactory 建立 SqlSession,SqlSession 是 MyBatis 暴露給外部使用的統一接口層,全部和數據庫打交道的操做都經過 SqlSession 這層。mybatis
下面經過時序圖描述 SqlSession 對象的建立流程:架構
MapperProxy
第三步,經過 SqlSession 得到 StudentMapper 對象,爲了便於理解,下面經過時序圖描述 Mapper 對象的獲取流程。app
MapperRegistry 是 Configuration 的一個屬性,MapperRegistry 緩存了 MapperProxyFactory 的 Map 集合,也就是說在解析完配置文件後,knownMappers 集合數據已經在 Configuration 對象中存在了。函數
問題一:爲何 StudentMapper(接口) 能夠調用方法?
經過斷點調試來看看 StudentMapper 的真實對象是什麼:
MapperProxy 類的代碼以下:
MapperProxyFactory 的 newInstance 方法實現代碼以下:
經過上面的源碼內容,能夠得出以下結論:
- StudentMapper 的真實對象是 MapperProxy;
- MapperProxy 繼承 InvocationHandler,實現 invoke 方法;
- MapperProxyFactory 的 newInstance 方法,經過 JDK 動態代理的方式建立了一個 MapperProxy 的代理類;
MyBatis 的 Mapper 是經過動態代理實現的,調用 Mapper 的任何方法都會執行 MapperProxy 的 invoke 方法。
MyBatis 使用的動態代理和一般的動態代理有點區別,沒有實現類,只有接口。
動態代理類圖結構以下所示:
MyBatis 動態代理類圖結構以下所示:
selectByPrimaryKey
第四部,執行 selectByPrimaryKey 查詢學生信息。在第8行代碼上面打上斷點,調試選擇「step into」,進入子函數內部,調用 org.apache.ibatis.binding.MapperProxy#invoke 方法。
MapperMethod 經過以下方式建立。
- mapperInterface:interface com.yjw.mybatis.dao.StudentMapper;
- method:invoke 方法中傳進來的 method 類;
- Configuration:MyBatis 的核心類,全部配置信息都存在該類中;
接着執行 mapperMethod.execute(sqlSession, args) 方法。
接下來仍是走到 SqlSession 類中,調用 selectOne 方法。繼續往下執行,會執行 Executor 的 query 方法。
經過上面的分析,簡單總結一下,咱們能夠抽象出 MyBatis 在執行一條 SQL 查詢的過程當中涉及到的主要類:Configuration、SqlSession、MapperProxy、Exector,根據這些類畫出以下 MyBatis 執行 SQL 的時序圖:
- 第一步是獲取 MapperProxy 代理對象;
- 第二步是根據獲取的代理對象,執行查詢操做;
Exector
SqlSession 中的 JDBC 操做部分最終都會委派給 Exector 實現,接着上面的斷點往下執行,進入 Exector 的 query 方法。
下面經過時序圖描述 Exector 的執行流程,真實的調用鏈路類比較多,這裏簡化了調用鏈路,省略了一些裝飾類、代理類,便於理解:
根據 Exector 執行的時序圖,能夠抽象出的主要類是:SqlSession、Exector、StatementHandler、ParameterHandler、ResultSetHandler。
StatementHandler 接口是 MyBatis 的核心接口之一,它是 Exector 接口實現的基礎。StatementHandler 的主要功能不少,例如建立 Statement 對象,爲 SQL 語句綁定實參,執行 SQL 語句,將結果集映射成結果對象。
StatementHandler 類中包含了 ParameterHandler 和 ResultSetHandler 的屬性。ParameterHandler 的主要功能是爲 SQL 語句綁定實參,也就是使用傳入的參數替換 SQL 語句中的「?」佔位符。ResultSetHandler 的主要功能是將結果集映射成結果對象。
參考 BaseStatementHandler 類:org.apache.ibatis.executor.statement.BaseStatementHandler
結論
根據上面的分析,抽象出的主要類有:Configuration、SqlSession、MapperProxy、Exector、StatementHandler、ParameterHandler、ResultSetHandler。
MyBatis 源碼篇