1、前言
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。我們在使用 Mybaits 進行開發 ,通常只需要定義幾個 Mapper 接口,然後在編寫一個 xml 文件,我們在配置文件中寫好 sql , Mybatis 幫我們完成 Mapper 接口到具體實現的調用。以及將結果映射到 model bean 中。
但是,對於 Mapper 接口,我們並沒有編寫其實現類!Mybatis是如何找到其實現類,進而完成具體的 CRUD 方法調用的呢?原理何在?
通常我們使用 Mybatis (Mybatis 單獨使用,而不是整合 spring )的主要步驟是:
- 構建 SqlSessionFactory ( 通過 xml 配置文件 , 或者直接編寫Java代碼)
- 從 SqlSessionFactory 中獲取 SqlSession
- 從 SqlSession 中獲取 Mapper
- 調用 Mapper的方法 ,例如:userMapper.selectUser(int userId);
2、Mapper 接口是怎麼找到實現類的
2.1、Mapper 接口註冊
諸如UserMapper之類的Mapper接口被添加到了MapperRegistry 中的一個HashMap中。並以 Mapper 接口的 Class 對象作爲 Key , 以一個攜帶Mapper接口作爲屬性的MapperProxyFactory 實例作爲value 。
2.2、生成Mapper接口的動態代理類
最終是通過Proxy.newProxyInstance產生了一個UserMapper的代理對象。Mybatis 爲了完成 Mapper 接口的實現,運用了代理模式。具體是使用了JDK動態代理,這個Proxy.newProxyInstance方法生成代理類的三個要素是:
- ClassLoader —— 指定當前接口的加載器即可
- 當前被代理的接口是什麼 —— 這裏就是 BlogMapper
- 代理類是什麼 —— 這裏就是 MapperProxy
代理模式中,代理類(MapperProxy)中才真正的完成了方法調用的邏輯。
MapperProxy的代碼如下:
public class MapperProxy<T> implements InvocationHandler, Serializable {
// 實現了InvocationHandler
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理以後,所有Mapper的方法調用時,都會調用這個invoke方法
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 使用了緩存
final MapperMethod mapperMethod = cachedMapperMethod(method);
//執行CURD
return mapperMethod.execute(sqlSession, args);
}
}
再往下一層,就是執行JDBC那一套了,獲取鏈接,執行,得到ResultSet,解析ResultSet映射成JavaBean。
3、總結
Mapper接口是如何產生動態代理對象的,Maper接口方法最終是如何執行的。總結起來主要就是這幾個點:
- Mapper 接口在初始SqlSessionFactory 註冊的。
- Mapper 接口註冊在了名爲 MapperRegistry 類的 HashMap中, key = Mapper class對象; value = 創建當前Mapper的工廠。
- Mapper 註冊之後,可以從SqlSession中get
- SqlSession.getMapper 運用了 JDK動態代理,產生了目標Mapper接口的代理對象。
- 動態代理的 代理類是 MapperProxy ,這裏邊最終完成了增刪改查方法的調用。