1. 程式人生 > >面試常問:Mybatis使用了哪些設計模式?

面試常問:Mybatis使用了哪些設計模式?

## 前言 雖然我們都知道有26個設計模式,但是大多停留在概念層面,真實開發中很少遇到,Mybatis原始碼中使用了大量的設計模式,閱讀原始碼並觀察設計模式在其中的應用,能夠更深入的理解設計模式。 ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec2272360cbd4e5c953abaf71c6bfad6~tplv-k3u1fbpfcp-watermark.image) **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中怎樣應用了該模式。 ## 一、Builder模式 Builder模式的定義是“將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。”,它屬於建立類模式,一般來說,如果一個物件的構建比較複雜,超出了建構函式所能包含的範圍,就可以使用工廠模式和Builder模式,相對於工廠模式會產出一個完整的產品,Builder應用於更加複雜的物件的構建,甚至只會構建產品的一個部分。 ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f033f89e65f14a048d4a72f8503c4558~tplv-k3u1fbpfcp-watermark.image) 在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為例,它包含以下方法: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a65534e2c9584f3abb63017293b80275~tplv-k3u1fbpfcp-watermark.image) 即根據不同的輸入引數來構建SqlSessionFactory這個工廠物件。 ## 二、工廠模式 在Mybatis中比如SqlSessionFactory使用的是工廠模式,該工廠沒有那麼複雜的邏輯,是一個簡單工廠模式。 簡單工廠模式(Simple Factory Pattern):又稱為靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式。在簡單工廠模式中,可以根據引數的不同返回不同類的例項。簡單工廠模式專門定義一個類來負責建立其他類的例項,被建立的例項通常都具有共同的父類。 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f1d47acfcd8c45d88455686cf5c5523e~tplv-k3u1fbpfcp-watermark.image) SqlSession可以認為是一個Mybatis工作的核心的介面,通過這個介面可以執行執行SQL語句、獲取Mappers、管理事務。類似於連線MySQL的Connection物件。 ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32c4c38338704350af2c66f64572b663~tplv-k3u1fbpfcp-watermark.image) 可以看到,該Factory的openSession方法過載了很多個,分別支援autoCommit、Executor、Transaction等引數的輸入,來構建核心的SqlSession物件。 在DefaultSqlSessionFactory的預設工廠實現裡,有一個方法可以看出工廠怎麼產出一個產品: ``` private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call // close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } ``` 這是一個openSession呼叫的底層方法,該方法先從configuration讀取對應的環境配置,然後初始化TransactionFactory獲得一個Transaction物件,然後通過Transaction獲取一個Executor物件,最後通過configuration、Executor、是否autoCommit三個引數構建了SqlSession。 在這裡其實也可以看到端倪,SqlSession的執行,其實是委託給對應的Executor來進行的。 **而對於LogFactory,它的實現程式碼:** ``` public final class LogFactory { private static Constructor logConstructor; private LogFactory() { // disable construction } public static Log getLog(Class aClass) { return getLog(aClass.getName()); } ``` 這裡有個特別的地方,是Log變數的的型別是Constructor,也就是說該工廠生產的不只是一個產品,而是具有Log公共介面的一系列產品,比如Log4jImpl、Slf4jImpl等很多具體的Log。 ## 三、單例模式 單例模式(Singleton Pattern):單例模式確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項,這個類稱為單例類,它提供全域性訪問的方法。 單例模式的要點有三個:一是某個類只能有一個例項;二是它必須自行建立這個例項;三是它必須自行向整個系統提供這個例項。單例模式是一種物件建立型模式。單例模式又名單件模式或單態模式。 ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f02433b119034becbdba4e2e016f1ac9~tplv-k3u1fbpfcp-watermark.image) 在Mybatis中有兩個地方用到單例模式,ErrorContext和LogFactory,其中ErrorContext是用在每個執行緒範圍內的單例,用於記錄該執行緒的執行環境錯誤資訊,而LogFactory則是提供給整個Mybatis使用的日誌工廠,用於獲得針對專案配置好的日誌物件。 **ErrorContext的單例實現程式碼:** ``` public class ErrorContext { private static final Thr