Mybatis源码阅读之二——模板方法模式与Executor
[系列目錄](méi)
Mybatis源碼閱讀之一——工廠模式與SqlSessionFactory
文章目錄
- 一. 模板方法模式
- 二. 同步回調(diào)與匿名函數(shù)
- 三. Executor
- BaseExecutor與其子類(lèi)
- 一二級(jí)緩存與CacheExecutor
- 總結(jié)
Mybatis中的Executor應(yīng)用了模板方法模式,我們有必要先來(lái)了解這一種行為型設(shè)計(jì)模式。
一. 模板方法模式
行為型模式是對(duì)在不同的對(duì)象之間劃分責(zé)任和算法的抽象化。
通過(guò)行為型模式,可以更加清晰地劃分類(lèi)與對(duì)象的職責(zé),并研究系統(tǒng)在運(yùn)行時(shí)實(shí)例對(duì)象 之間的交互。
模板方法模式通過(guò)繼承關(guān)系在父類(lèi)與子類(lèi)之間分配行為,父類(lèi)負(fù)責(zé)通用邏輯的實(shí)現(xiàn),不同的邏輯則是提供統(tǒng)一的方法,交由子類(lèi)分別繼承實(shí)現(xiàn)。
-
實(shí)現(xiàn)一
BaseService抽象類(lèi)實(shí)現(xiàn)了doAction01方法,doAction02方法為abstract或者內(nèi)部實(shí)現(xiàn)為直接拋異常。
-
實(shí)現(xiàn)二
使用java提供的default關(guān)鍵字,用接口替換抽象類(lèi)來(lái)使用。
總結(jié): 通過(guò)如上兩種實(shí)現(xiàn)方式,可以讓子類(lèi)在不改變算法整體結(jié)構(gòu)的情況下,重新定義算法中的某些
步驟。這種模式在我們有大量重復(fù)邏輯的,不同類(lèi)型又有些區(qū)別差異的時(shí)候顯然是十分適合的。
比如前文【Mybatis源碼閱讀之一——工廠模式與SqlSessionFactory】有提到的采購(gòu)單與銷(xiāo)售單各自的處理邏輯有大量相同代碼,這部分可以提取到模板類(lèi),而剩余有區(qū)別的邏輯提取出來(lái),作為一個(gè)差異方法來(lái)各自實(shí)現(xiàn)。
二. 同步回調(diào)與匿名函數(shù)
同步回調(diào)能夠做到與模板方法模式類(lèi)似的事情,前者更加靈活。
借鑒【設(shè)計(jì)模式之美】中的例子,spring中的JdbcTemplate雖然名字上帶了模板,但是其實(shí)內(nèi)部實(shí)現(xiàn)使用的是同步回調(diào)。
如JdbcTemplate.query()方法,以及batchUpdate方法,最終都使用了execute()方法,而他們自身應(yīng)有的邏輯則是作為回調(diào)傳遞給了execute(),實(shí)現(xiàn)了自身邏輯的抽離,使得execute()方法有了模板類(lèi)的作用。
值得一提的是,JdbcTemplate使用的是回調(diào)類(lèi),如果我們自己做開(kāi)發(fā),能夠精簡(jiǎn)成匿名函數(shù)的話,個(gè)人認(rèn)為易讀性會(huì)更好一些。
關(guān)于JAVA中匿名函數(shù)的使用可以參考這篇https://blog.csdn.net/qq_35946969/article/details/108492198
三. Executor
回到Mybatis源碼部分,從類(lèi)圖看,CachingExecutor除外,Executor->BaseExecutor->各個(gè)實(shí)現(xiàn)類(lèi),這個(gè)結(jié)構(gòu)是一個(gè)經(jīng)典的模板方法模式,比我們上述部分中,抽象類(lèi)的上方多了一層接口。
先看一下Executor的接口的各方法以及大致功能描述:
這些方法我們分開(kāi)說(shuō),先來(lái)看CRUD。
BaseExecutor與其子類(lèi)
- BaseExecutor.update()
當(dāng)我們想要繼續(xù)找doUpdate時(shí),卻發(fā)現(xiàn)這是一個(gè)抽象接口,這就是模板方法的具體應(yīng)用,doUpdate的實(shí)現(xiàn)交給了子類(lèi),看一下他們各自的實(shí)現(xiàn)。 - SimpleExecutor
由這里面的方法可得知,BaseExecutor留給子類(lèi)實(shí)現(xiàn)的方法并不多,主要是CRUD和doFlushStatements。對(duì)于SimpleExecutor的doUpdate來(lái)說(shuō)做了簡(jiǎn)單兩件事情。- 第一步,通過(guò)configuration生成StatementHandler,簡(jiǎn)單說(shuō)一下,configuration是mybatis的全局配置類(lèi),幾乎所有的配置項(xiàng)都放在這個(gè)類(lèi)。
- 第二步,使用JDBC(對(duì)應(yīng)的數(shù)據(jù)庫(kù)驅(qū)動(dòng))的Statement進(jìn)行sql執(zhí)行。這里不再深入,關(guān)于JDBC和Mybatis中的相關(guān)Handler內(nèi)容過(guò)多,我們下一章再來(lái)詳細(xì)分析
- BatchExecutor
顧名思義,是SimpleExecutor的批量版。
- 第一步,如果是復(fù)用statement,只需要存儲(chǔ)參數(shù)。
- 第二步,如果是新的statement,存儲(chǔ)下來(lái)。
- 第三步,通過(guò)JDBC的Statement.addBatch()方法將statement存儲(chǔ),等待之后的批量提交。
- ReuseExecutor
是SimpleExecutor的可復(fù)用版,此類(lèi)的關(guān)鍵就在statementMap這個(gè)全局變量,他會(huì)緩存sql->Statement的對(duì)應(yīng)關(guān)系,邏輯簡(jiǎn)單不再展開(kāi)。
一二級(jí)緩存與CacheExecutor
回到Executor的接口,我們繼續(xù)往下看緩存相關(guān)的接口:
上面我們介紹了BaseExecutor的子類(lèi)只有CRUD等幾個(gè)少數(shù)方法,緩存相關(guān)的接口只在BaseExecutor/CacheExecutor實(shí)現(xiàn)。
-
CachingExecutor.createCacheKey()
可以看到CachingExecutor什么都沒(méi)做,直接轉(zhuǎn)給了自己內(nèi)部的Executor代理類(lèi),那么具體的實(shí)現(xiàn)只會(huì)在BaseExecutor中了。
值得一提,這里的代理Executor體現(xiàn)了CacheExecutor的作用,他其實(shí)只是一個(gè)BaseExecutor的裝飾器,在BaseExecutor的基礎(chǔ)上,增加了二級(jí)緩存功能 -
BaseExecutor.createCacheKey()
這里的實(shí)現(xiàn)其實(shí)就是將sql等一些信息組裝起來(lái),作為一個(gè)CacheKey對(duì)象返回,用于之后做一級(jí)緩存/二級(jí)緩存的key。
-
BaseExecutor.clearLocalCache()
CachingExecutor中該方法仍然是直接調(diào)用代理Executor,我們直接看BaseExecutor。
這里的localCache其實(shí)就是我們所說(shuō)的一級(jí)緩存,PerpetualCache可以直接看作一個(gè)Map結(jié)構(gòu)(Cache相關(guān)的具體涉及我們放到后面與裝飾器一起解讀) -
一級(jí)緩存
讓我們?cè)僬艺疫@個(gè)一級(jí)緩存被使用到的地方。- 查詢(xún)時(shí):
- 從數(shù)據(jù)庫(kù)查出時(shí)更新:
-
二級(jí)緩存
之前我們提到了二級(jí)緩存的實(shí)現(xiàn)就是CacheExecutor。
查詢(xún)時(shí),會(huì)先經(jīng)過(guò)二級(jí)緩存,找不到才會(huì)進(jìn)入代理(原始)Executor。
總結(jié)
Executor與BaseExecutor采用了模板方法模式,擴(kuò)展了批量操作,單個(gè)操作,復(fù)用操作等子類(lèi),同時(shí)又使用CacheExecutor裝飾器模式做了二級(jí)緩存的實(shí)現(xiàn)。
歡迎關(guān)注微信公眾號(hào) 【JAVA技術(shù)分享官】,公眾號(hào)首發(fā),持續(xù)輸出原創(chuàng)高質(zhì)量JAVA開(kāi)發(fā)者知識(shí)點(diǎn)
總結(jié)
以上是生活随笔為你收集整理的Mybatis源码阅读之二——模板方法模式与Executor的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: vue3学习(引入轮播图插件)
- 下一篇: 显卡显存测试u盘 mats_与RX570