為啥Mybatis的介面不需要實現類,你真的有了解過嗎?

在使用mybatis框架時,我們發現mapper介面是沒有實現類的,取而代之的是一個xml檔案。也就是說我們呼叫mapper介面,實際上是使用了對應的mapper。xml中定義sql語句完成資料操作的。

那麼我們有沒有想過,為什麼mapper介面沒有實現類,它是如何和xml關聯起來的?

我們在學習java 基礎時,知道介面是不可以直接呼叫的,只有透過定義實現類了實現介面,然後new一個實現類物件,再將實現類物件賦給介面宣告的物件。呼叫介面的方法實際上就是呼叫被引用物件的方法,也就是實現類實現的方法。那麼為啥Mybatis的介面不需要實現類呢?

下面我們就以查詢所有員工資料為例,進行一步一步的除錯,檢視底層是怎麼操作的呢?

EmployeeMapper介面

EmployeeMapper。xml檔案的sql語句

那麼getAllEmp方法被呼叫的時候,被引用的物件是誰呢?介面被呼叫時候發生了什麼?

我們先來回答第二個問題,既然找不到實現類,EmployeeMapper有沒可能被代理起來呢,getAllEmp方法呼叫時候,我們找到代理物件來執行就行了。

在學習代理模式時,有一個動態代理模式,透過動態代理介面的所有方法,每次介面被呼叫,就會進入動態代理物件的invoke方法,然後載入xml中的sql完成操作資料庫,再返回結果。

那麼Mybatis 的Mapper介面有沒有可能被動態代理物件來實現,完成後面的操作呢?

接下來我們進入debug模式來除錯看看。

(1) 在sqlSession。getMapper()方法這裡設定一個斷點

(2)繼續走到Configuration類的getMapper方法裡,Configuration主要儲存 Mybatis 所有的配置資訊,包括mybatis配置檔案、EmployeeMapper。xml等檔案都會被預先載入到Configuration裡。

這時候,我們發現Configuration裡面出現了一個mapperRegistry,這個是mapper的註冊器,其實在載入EmployeeMapper。xml的時候,我們就需要在mapperRegistry裡面進行註冊,所以,我們可以從這裡進行獲取。繼續走~

(3)進入到mapperRegistry的getMapper方法

這裡分為了兩步執行:

(1)knownMappers。get(type);

獲取已知的載入過的mapper中獲取出mapper代理工廠

(2)mapperProxyFactory。newInstance(sqlSession);

代理工廠生成動態代理返回

那麼knownMappers其實是個map集合,根據EmployeeMapper。class獲取MapperProxyFactory,所以knownMappers必然是原始碼前面的步驟中set進去的。我們先找找,到底是哪裡set進去的呢,繼續除錯我們發現XMLMapperBuilder類的bindMapperForNamespace方法

這個方法裡面的boundType,是透過Resources。classForName(namespace);生成的class,Resources。classForName底層其實就是呼叫Class。forName生成的反射物件,而引數是namespace,namespacne就是com。mybatis3。mappers。EmployeeMapper:

Class。forName(com。mybatis3。mappers。EmployeeMapper)生成反射物件。生成的boundType在被configuration。addMapper(boundType);可以看到在這裡呼叫了put方法放進去的。

(4) 接下來再回到mapperRegistry的getMapper方法繼續往下除錯進入到MapperProxyFactory的newInstance裡面,看到該方法return newInstance(mapperProxy)回來,繼續往下除錯

(5)進入到newInstance這個方法裡面我們看到了Proxy。newProxyInstance程式碼,就是JDK建立代理的方法

到這裡我們發現是呼叫了JDK動態代理的newProxyInstance方法。

(6)那麼我們來看看是如何代理的,開啟MapperProxy這個類,找到裡面的invoke方法

在這個方法裡面MapperMethod 類有兩個重點,其一就是初始化,其二就是執行SQL,為什麼說初始化很重要呢?因為初始化的時候需要透過介面名全稱+方法全稱去 Configuration 找我們之前載入的SQL,這也就是為什麼介面方法定義和SQL的ID必須保持一致的原因;其二執行SQL。

(7)接著MapperMethod 呼叫 execute 執行 SQL,程式碼很簡單如下

在Case:SELECT下 我們找到

result = this。executeForMany(sqlSession, args);

(8)我們點開executeForMany方法

最終我們發現是呼叫了sqlSession。selectList方法,然後根據方法名找到EmployeeMapper。xml檔案中對應id標識,執行sql語句完成了查詢操作。

總結

今天我們從認為Mapper介面是創建出來的代理物件完成了查詢操作出發,一步步進行了除錯發現,最終Mybatis的介面不需要實現類,實際上是使用了JDK動態代理的方式建立代理物件完成的。

相關文章