Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

雖然我們都知道有26個設計模式,但是大多停留在概念層面,真實開發中很少遇到,Mybatis源碼中使用了大量的設計模式,閱讀源碼並觀察設計模式在其中的應用,能夠更深入的理解設計模式。

Mybatis至少遇到了以下的設計模式的使用:

  1. Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
  2. 工廠模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
  3. 單例模式,例如ErrorContext和LogFactory;
  4. 代理模式,Mybatis實現的核心,比如MapperProxy、ConnectionLogger,用的jdk的動態代理;還有executor.loader包使用了cglib或者javassist達到延遲加載的效果;
  5. 組合模式,例如SqlNode和各個子類ChooseSqlNode等;
  6. 模板方法模式,例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和所有的子類例如IntegerTypeHandler;
  7. 適配器模式,例如Log的Mybatis接口和它對jdbc、log4j等各種日誌框架的適配實現;
  8. 裝飾者模式,例如Cache包中的cache.decorators子包中等各個裝飾者的實現;
  9. 迭代器模式,例如迭代器模式PropertyTokenizer;

接下來挨個模式進行解讀,先介紹模式自身的知識,然後解讀在Mybatis中怎樣應用了該模式。

1、Builder模式

Builder模式的定義是“將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。”,它屬於創建類模式,一般來說,如果一個對象的構建比較複雜,超出了構造函數所能包含的範圍,就可以使用工廠模式和Builder模式,相對於工廠模式會產出一個完整的產品,Builder應用於更加複雜的對象的構建,甚至只會構建產品的一個部分。

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

在Mybatis環境的初始化過程中,SqlSessionFactoryBuilder會調用XMLConfigBuilder讀取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,構建Mybatis運行的核心對象Configuration對象,然後將該Configuration對象作為參數構建一個SqlSessionFactory對象。

其中XMLConfigBuilder在構建Configuration對象時,也會調用XMLMapperBuilder用於讀取*Mapper文件,而XMLMapperBuilder會使用XMLStatementBuilder來讀取和build所有的SQL語句。

在這個過程中,有一個相似的特點,就是這些Builder會讀取文件或者配置,然後做大量的XpathParser解析、配置或語法的解析、反射生成對象、存入結果緩存等步驟,這麼多的工作都不是一個構造函數所能包括的,因此大量採用了Builder模式來解決。

對於builder的具體類,方法都大都用build*開頭,比如SqlSessionFactoryBuilder為例,它包含以下方法:

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

即根據不同的輸入參數來構建SqlSessionFactory這個工廠對象。

2、工廠模式

在Mybatis中比如SqlSessionFactory使用的是工廠模式,該工廠沒有那麼複雜的邏輯,是一個簡單工廠模式。

簡單工廠模式(Simple Factory Pattern):又稱為靜態工廠方法(Static Factory Method)模式,它屬於類創建型模式。在簡單工廠模式中,可以根據參數的不同返回不同類的實例。簡單工廠模式專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

SqlSession可以認為是一個Mybatis工作的核心的接口,通過這個接口可以執行執行SQL語句、獲取Mappers、管理事務。類似於連接MySQL的Connection對象。

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

可以看到,該Factory的openSession方法重載了很多個,分別支持autoCommit、Executor、Transaction等參數的輸入,來構建核心的SqlSession對象。

在DefaultSqlSessionFactory的默認工廠實現裡,有一個方法可以看出工廠怎麼產出一個產品:

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

這是一個openSession調用的底層方法,該方法先從configuration讀取對應的環境配置,然後初始化TransactionFactory獲得一個Transaction對象,然後通過Transaction獲取一個Executor對象,最後通過configuration、Executor、是否autoCommit三個參數構建了SqlSession。在這裡其實也可以看到端倪,SqlSession的執行,其實是委託給對應的Executor來進行的。

而對於LogFactory,它的實現代碼:

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

這裡有個特別的地方,是Log變量的的類型是Constructor extends Log>,也就是說該工廠生產的不只是一個產品,而是具有Log公共接口的一系列產品,比如Log4jImpl、Slf4jImpl等很多具體的Log。

3、單例模式

單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。

單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象創建型模式。單例模式又名單件模式或單態模式。

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

在Mybatis中有兩個地方用到單例模式,ErrorContext和LogFactory,其中ErrorContext是用在每個線程範圍內的單例,用於記錄該線程的執行環境錯誤信息,而LogFactory則是提供給整個Mybatis使用的日誌工廠,用於獲得針對項目配置好的日誌對象。

ErrorContext的單例實現代碼:

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

構造函數是private修飾,具有一個static的局部instance變量和一個獲取instance變量的方法,在獲取實例的方法中,先判斷是否為空如果是的話就先創建,然後返回構造好的對象。

只是這裡有個有趣的地方是,LOCAL的靜態實例變量使用了ThreadLocal修飾,也就是說它屬於每個線程各自的數據,而在instance()方法中,先獲取本線程的該實例,如果沒有就創建該線程獨有的ErrorContext。

4、代理模式

代理模式可以認為是Mybatis的核心使用的模式,正是由於這個模式,我們只需要編寫Mapper.java接口,不需要實現,由Mybatis後臺幫我們完成具體SQL的執行。

代理模式(Proxy Pattern) :給某一個對象提供一個代 理,並由代理對象控制對原對象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一種對象結構型模式。

代理模式包含如下角色:

  • Subject: 抽象主題角色
  • Proxy: 代理主題角色
  • RealSubject: 真實主題角色
Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

這裡有兩個步驟,第一個是提前創建一個Proxy,第二個是使用的時候會自動請求Proxy,然後由Proxy來執行具體事務;

當我們使用Configuration的getMapper方法時,會調用mapperRegistry.getMapper方法,而該方法又會調用mapperProxyFactory.newInstance(sqlSession)來生成一個具體的代理:

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

在這裡,先通過T newInstance(SqlSession sqlSession)方法會得到一個MapperProxy對象,然後調用T newInstance(MapperProxy mapperProxy)生成代理對象然後返回。

而查看MapperProxy的代碼,可以看到如下內容:

Mybatis 使用的 9 種設計模式詳解,看看能給你帶來哪些啟發?

非常典型的,該MapperProxy類實現了InvocationHandler接口,並且實現了該接口的invoke方法。

通過這種方式,我們只需要編寫Mapper.java接口類,當真正執行一個Mapper接口的時候,就會轉發給MapperProxy.invoke方法,而該方法則會調用後續的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的執行和返回。

關注查看另外5種設計模式

原文:http://www.crazyant.net/2022.html


分享到:


相關文章: