面试官问你MyBatis中有哪些设计模式,把这篇文章发给他
戳藍字“CSDN云計算”關注我們哦!
作者 | 瘋狂的螞蟻
來源?|?https://dwz.cn/KFgol1De
之前總結過一篇Spring中用到了哪些設計模式:《面試官:“談談Spring中都用到了那些設計模式?”》,昨晚看到了一篇很不錯的一篇介紹MyBatis中都用到了那些設計模式的文章,今天分享給各位。
雖然我們都知道有26個設計模式,但是大多停留在概念層面,真實開發中很少遇到,Mybatis源碼中使用了大量的設計模式,閱讀源碼并觀察設計模式在其中的應用,能夠更深入的理解設計模式。
Mybatis至少遇到了以下的設計模式的使用:
Builder模式?:
例如?SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;
工廠模式?:
例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;
單例模式?:例如ErrorContext和LogFactory;
代理模式?:Mybatis實現的核心,比如MapperProxy、ConnectionLogger,用的jdk的動態代理;還有executor.loader包使用了cglib或者javassist達到延遲加載的效果;
組合模式?:例如SqlNode和各個子類ChooseSqlNode等;
模板方法模式?: 例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和所有的子類例如IntegerTypeHandler;
適配器模式?: 例如Log的Mybatis接口和它對jdbc、log4j等各種日志框架的適配實現;
裝飾者模式?: 例如cache包中的cache.decorators子包中等各個裝飾者的實現;
迭代器模式?: 例如迭代器模式PropertyTokenizer;
接下來挨個模式進行解讀,先介紹模式自身的知識,然后解讀在Mybatis中怎樣應用了該模式。
1、Builder 模式
Builder模式的定義是“將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。”,它屬于創建類模式,一般來說,如果一個對象的構建比較復雜,超出了構造函數所能包含的范圍,就可以使用工廠模式和Builder模式,相對于工廠模式會產出一個完整的產品,Builder應用于更加復雜的對象的構建,甚至只會構建產品的一個部分。《effective-java》中第2條也提到:遇到多個構造器參數時,考慮用構建者(Builder)模式。
在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為例,它包含以下方法:
即根據不同的輸入參數來構建SqlSessionFactory這個工廠對象。
2、工廠模式
在Mybatis中比如SqlSessionFactory使用的是工廠模式,該工廠沒有那么復雜的邏輯,是一個簡單工廠模式。
簡單工廠模式(Simple Factory Pattern):又稱為靜態工廠方法(Static Factory Method)模式,它屬于類創建型模式。在簡單工廠模式中,可以根據參數的不同返回不同類的實例。簡單工廠模式專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類。
SqlSession可以認為是一個Mybatis工作的核心的接口,通過這個接口可以執行執行SQL語句、獲取Mappers、管理事務。類似于連接MySQL的Connection對象。
可以看到,該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();????????}????}????????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<??extends?Log>?logConstructor;????private?LogFactory()?{????????//?disable?construction????}????public?static?Log?getLog(Class<?>?aClass)?{????????return?getLog(aClass.getName());????}final?class?LogFactory?{????private?static?Constructor<??extends?Log>?logConstructor;
????private?LogFactory()?{
????????//?disable?construction
????}
????public?static?Log?getLog(Class<?>?aClass)?{
????????return?getLog(aClass.getName());
????}
這里有個特別的地方,是Log變量的的類型是Constructor<? extendsLog>,也就是說該工廠生產的不只是一個產品,而是具有Log公共接口的一系列產品,比如Log4jImpl、Slf4jImpl等很多具體的Log。
3、單例模式
單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。
單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象創建型模式。單例模式又名單件模式或單態模式。
在Mybatis中有兩個地方用到單例模式,ErrorContext和LogFactory,其中ErrorContext是用在每個線程范圍內的單例,用于記錄該線程的執行環境錯誤信息,而LogFactory則是提供給整個Mybatis使用的日志工廠,用于獲得針對項目配置好的日志對象。
ErrorContext的單例實現代碼:
public?class?ErrorContext?{????private?static?final?ThreadLocal<ErrorContext>?LOCAL?=?new?ThreadLocal<ErrorContext>();????private?ErrorContext()?{????}????public?static?ErrorContext?instance()?{????????ErrorContext?context?=?LOCAL.get();????????if?(context?==?null)?{????????????context?=?new?ErrorContext();????????????LOCAL.set(context);????????}????????return?context;????}class?ErrorContext?{????private?static?final?ThreadLocal<ErrorContext>?LOCAL?=?new?ThreadLocal<ErrorContext>();
????private?ErrorContext()?{
????}
????public?static?ErrorContext?instance()?{
????????ErrorContext?context?=?LOCAL.get();
????????if?(context?==?null)?{
????????????context?=?new?ErrorContext();
????????????LOCAL.set(context);
????????}
????????return?context;
????}
構造函數是private修飾,具有一個static的局部instance變量和一個獲取instance變量的方法,在獲取實例的方法中,先判斷是否為空如果是的話就先創建,然后返回構造好的對象。
只是這里有個有趣的地方是,LOCAL的靜態實例變量使用了ThreadLocal修飾,也就是說它屬于每個線程各自的數據,而在instance()方法中,先獲取本線程的該實例,如果沒有就創建該線程獨有的ErrorContext。
4、代理模式
代理模式可以認為是Mybatis的核心使用的模式,正是由于這個模式,我們只需要編寫Mapper.java接口,不需要實現,由Mybatis后臺幫我們完成具體SQL的執行。
代理模式(Proxy Pattern) :給某一個對象提供一個代 理,并由代理對象控制對原對象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一種對象結構型模式。
代理模式包含如下角色:
這里有兩個步驟,第一個是提前創建一個Proxy,第二個是使用的時候會自動請求Proxy,然后由Proxy來執行具體事務;
當我們使用Configuration的getMapper方法時,會調用mapperRegistry.getMapper方法,而該方法又會調用mapperProxyFactory.newInstance(sqlSession)來生成一個具體的代理:
/**?*?@author?Lasse?Voss?*/public?class?MapperProxyFactory<T>?{????private?final?Class<T>?mapperInterface;????private?final?Map<Method,?MapperMethod>?methodCache?=?new?ConcurrentHashMap<Method,?MapperMethod>();????public?MapperProxyFactory(Class<T>?mapperInterface)?{????????this.mapperInterface?=?mapperInterface;????}????public?Class<T>?getMapperInterface()?{????????return?mapperInterface;????}????public?Map<Method,?MapperMethod>?getMethodCache()?{????????return?methodCache;????}????@SuppressWarnings("unchecked")????protected?T?newInstance(MapperProxy<T>?mapperProxy)?{????????return?(T)?Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[]?{?mapperInterface?},????????????????mapperProxy);????}????public?T?newInstance(SqlSession?sqlSession)?{????????final?MapperProxy<T>?mapperProxy?=?new?MapperProxy<T>(sqlSession,?mapperInterface,?methodCache);????????return?newInstance(mapperProxy);????}}public?class?MapperProxyFactory<T>?{
????private?final?Class<T>?mapperInterface;
????private?final?Map<Method,?MapperMethod>?methodCache?=?new?ConcurrentHashMap<Method,?MapperMethod>();
????public?MapperProxyFactory(Class<T>?mapperInterface)?{
????????this.mapperInterface?=?mapperInterface;
????}
????public?Class<T>?getMapperInterface()?{
????????return?mapperInterface;
????}
????public?Map<Method,?MapperMethod>?getMethodCache()?{
????????return?methodCache;
????}
????@SuppressWarnings("unchecked")
????protected?T?newInstance(MapperProxy<T>?mapperProxy)?{
????????return?(T)?Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[]?{?mapperInterface?},
????????????????mapperProxy);
????}
????public?T?newInstance(SqlSession?sqlSession)?{
????????final?MapperProxy<T>?mapperProxy?=?new?MapperProxy<T>(sqlSession,?mapperInterface,?methodCache);
????????return?newInstance(mapperProxy);
????}
}
在這里,先通過T newInstance(SqlSession sqlSession)方法會得到一個MapperProxy對象,然后調用T newInstance(MapperProxy<T> mapperProxy)生成代理對象然后返回。
而查看MapperProxy的代碼,可以看到如下內容:
public?class?MapperProxy<T>?implements?InvocationHandler,?Serializable?{????@Override????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{????????try?{????????????if?(Object.class.equals(method.getDeclaringClass()))?{????????????????return?method.invoke(this,?args);????????????}?else?if?(isDefaultMethod(method))?{????????????????return?invokeDefaultMethod(proxy,?method,?args);????????????}????????}?catch?(Throwable?t)?{????????????throw?ExceptionUtil.unwrapThrowable(t);????????}????????final?MapperMethod?mapperMethod?=?cachedMapperMethod(method);????????return?mapperMethod.execute(sqlSession,?args);????}class?MapperProxy<T>?implements?InvocationHandler,?Serializable?{????@Override
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????try?{
????????????if?(Object.class.equals(method.getDeclaringClass()))?{
????????????????return?method.invoke(this,?args);
????????????}?else?if?(isDefaultMethod(method))?{
????????????????return?invokeDefaultMethod(proxy,?method,?args);
????????????}
????????}?catch?(Throwable?t)?{
????????????throw?ExceptionUtil.unwrapThrowable(t);
????????}
????????final?MapperMethod?mapperMethod?=?cachedMapperMethod(method);
????????return?mapperMethod.execute(sqlSession,?args);
????}
非常典型的,該MapperProxy類實現了InvocationHandler接口,并且實現了該接口的invoke方法。
通過這種方式,我們只需要編寫Mapper.java接口類,當真正執行一個Mapper接口的時候,就會轉發給MapperProxy.invoke方法,而該方法則會調用后續的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的執行和返回。
5、組合模式
組合模式組合多個對象形成樹形結構以表示“整體-部分”的結構層次。
組合模式對單個對象(葉子對象)和組合對象(組合對象)具有一致性,它將對象組織到樹結構中,可以用來描述整體與部分的關系。同時它也模糊了簡單元素(葉子對象)和復雜元素(容器對象)的概念,使得客戶能夠像處理簡單元素一樣來處理復雜元素,從而使客戶程序能夠與復雜元素的內部結構解耦。
在使用組合模式中需要注意一點也是組合模式最關鍵的地方:葉子對象和組合對象實現相同的接口。這就是組合模式能夠將葉子節點和對象節點進行一致處理的原因。
Mybatis支持動態SQL的強大功能,比如下面的這個SQL:
<update?id="update"?parameterType="org.format.dynamicproxy.mybatis.bean.User">????UPDATE?users????<trim?prefix="SET"?prefixOverrides=",">????????<if?test="name?!=?null?and?name?!=?''">????????????name?=?#{name}????????</if>????????<if?test="age?!=?null?and?age?!=?''">????????????,?age?=?#{age}????????</if>????????<if?test="birthday?!=?null?and?birthday?!=?''">????????????,?birthday?=?#{birthday}????????</if>????</trim>????where?id?=?${id}</update>????UPDATE?users
????<trim?prefix="SET"?prefixOverrides=",">
????????<if?test="name?!=?null?and?name?!=?''">
????????????name?=?#{name}
????????</if>
????????<if?test="age?!=?null?and?age?!=?''">
????????????,?age?=?#{age}
????????</if>
????????<if?test="birthday?!=?null?and?birthday?!=?''">
????????????,?birthday?=?#{birthday}
????????</if>
????</trim>
????where?id?=?${id}
</update>
在這里面使用到了trim、if等動態元素,可以根據條件來生成不同情況下的SQL;
在DynamicSqlSource.getBoundSql方法里,調用了rootSqlNode.apply(context)方法,apply方法是所有的動態節點都實現的接口:
public?interface?SqlNode?{????boolean?apply(DynamicContext?context);}interface?SqlNode?{????boolean?apply(DynamicContext?context);
}
對于實現該SqlSource接口的所有節點,就是整個組合模式樹的各個節點:
組合模式的簡單之處在于,所有的子節點都是同一類節點,可以遞歸的向下執行,比如對于TextSqlNode,因為它是最底層的葉子節點,所以直接將對應的內容append到SQL語句中:
????@Override????public?boolean?apply(DynamicContext?context)?{????????GenericTokenParser?parser?=?createParser(new?BindingTokenParser(context,?injectionFilter));????????context.appendSql(parser.parse(text));????????return?true;????}????public?boolean?apply(DynamicContext?context)?{
????????GenericTokenParser?parser?=?createParser(new?BindingTokenParser(context,?injectionFilter));
????????context.appendSql(parser.parse(text));
????????return?true;
????}
但是對于IfSqlNode,就需要先做判斷,如果判斷通過,仍然會調用子元素的SqlNode,即contents.apply方法,實現遞歸的解析。
@Overridepublic?boolean?apply(DynamicContext?context)?{????if?(evaluator.evaluateBoolean(test,?context.getBindings()))?{????????contents.apply(context);????????return?true;????}????return?false;}?public?boolean?apply(DynamicContext?context)?{
????if?(evaluator.evaluateBoolean(test,?context.getBindings()))?{
????????contents.apply(context);
????????return?true;
????}
????return?false;
}?
6、模板方法模式
模板方法模式是所有模式中最為常見的幾個模式之一,是基于繼承的代碼復用的基本技術。
模板方法模式需要開發抽象類和具體子類的設計師之間的協作。一個設計師負責給出一個算法的輪廓和骨架,另一些設計師則負責給出這個算法的各個邏輯步驟。代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本方法匯總起來的方法叫做模板方法(template method),這個設計模式的名字就是從此而來。
模板類定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
在Mybatis中,sqlSession的SQL執行,都是委托給Executor實現的,Executor包含以下結構:
其中的BaseExecutor就采用了模板方法模式,它實現了大部分的SQL執行邏輯,然后把以下幾個方法交給子類定制化完成:
????@Override????public?boolean?apply(DynamicContext?context)?{????????if?(evaluator.evaluateBoolean(test,?context.getBindings()))?{????????????contents.apply(context);????????????return?true;????????}????????return?false;????}????public?boolean?apply(DynamicContext?context)?{
????????if?(evaluator.evaluateBoolean(test,?context.getBindings()))?{
????????????contents.apply(context);
????????????return?true;
????????}
????????return?false;
????}
該模板方法類有幾個子類的具體實現,使用了不同的策略:
比如在SimpleExecutor中這樣實現update方法:
????@Override????public?int?doUpdate(MappedStatement?ms,?Object?parameter)?throws?SQLException?{????????Statement?stmt?=?null;????????try?{????????????Configuration?configuration?=?ms.getConfiguration();????????????StatementHandler?handler?=?configuration.newStatementHandler(this,?ms,?parameter,?RowBounds.DEFAULT,?null,????????????????????null);????????????stmt?=?prepareStatement(handler,?ms.getStatementLog());????????????return?handler.update(stmt);????????}?finally?{????????????closeStatement(stmt);????????}????}????public?int?doUpdate(MappedStatement?ms,?Object?parameter)?throws?SQLException?{
????????Statement?stmt?=?null;
????????try?{
????????????Configuration?configuration?=?ms.getConfiguration();
????????????StatementHandler?handler?=?configuration.newStatementHandler(this,?ms,?parameter,?RowBounds.DEFAULT,?null,
????????????????????null);
????????????stmt?=?prepareStatement(handler,?ms.getStatementLog());
????????????return?handler.update(stmt);
????????}?finally?{
????????????closeStatement(stmt);
????????}
????}
7、適配器模式
適配器模式(Adapter Pattern) :將一個接口轉換成客戶希望的另一個接口,適配器模式使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結構型模式,也可以作為對象結構型模式。
在Mybatsi的logging包中,有一個Log接口:
/**?*?@author?Clinton?Begin?*/public?interface?Log?{????boolean?isDebugEnabled();????boolean?isTraceEnabled();????void?error(String?s,?Throwable?e);????void?error(String?s);????void?debug(String?s);????void?trace(String?s);????void?warn(String?s);}public?interface?Log?{
????boolean?isDebugEnabled();
????boolean?isTraceEnabled();
????void?error(String?s,?Throwable?e);
????void?error(String?s);
????void?debug(String?s);
????void?trace(String?s);
????void?warn(String?s);
}
該接口定義了Mybatis直接使用的日志方法,而Log接口具體由誰來實現呢?Mybatis提供了多種日志框架的實現,這些實現都匹配這個Log接口所定義的接口方法,最終實現了所有外部日志框架到Mybatis日志包的適配:
比如對于Log4jImpl的實現來說,該實現持有了org.apache.log4j.Logger的實例,然后所有的日志方法,均委托該實例來實現。
public?class?Log4jImpl?implements?Log?{????private?static?final?String?FQCN?=?Log4jImpl.class.getName();????private?Logger?log;????public?Log4jImpl(String?clazz)?{????????log?=?Logger.getLogger(clazz);????}????@Override????public?boolean?isDebugEnabled()?{????????return?log.isDebugEnabled();????}????@Override????public?boolean?isTraceEnabled()?{????????return?log.isTraceEnabled();????}????@Override????public?void?error(String?s,?Throwable?e)?{????????log.log(FQCN,?Level.ERROR,?s,?e);????}????@Override????public?void?error(String?s)?{????????log.log(FQCN,?Level.ERROR,?s,?null);????}????@Override????public?void?debug(String?s)?{????????log.log(FQCN,?Level.DEBUG,?s,?null);????}????@Override????public?void?trace(String?s)?{????????log.log(FQCN,?Level.TRACE,?s,?null);????}????@Override????public?void?warn(String?s)?{????????log.log(FQCN,?Level.WARN,?s,?null);????}}class?Log4jImpl?implements?Log?{????private?static?final?String?FQCN?=?Log4jImpl.class.getName();
????private?Logger?log;
????public?Log4jImpl(String?clazz)?{
????????log?=?Logger.getLogger(clazz);
????}
????@Override
????public?boolean?isDebugEnabled()?{
????????return?log.isDebugEnabled();
????}
????@Override
????public?boolean?isTraceEnabled()?{
????????return?log.isTraceEnabled();
????}
????@Override
????public?void?error(String?s,?Throwable?e)?{
????????log.log(FQCN,?Level.ERROR,?s,?e);
????}
????@Override
????public?void?error(String?s)?{
????????log.log(FQCN,?Level.ERROR,?s,?null);
????}
????@Override
????public?void?debug(String?s)?{
????????log.log(FQCN,?Level.DEBUG,?s,?null);
????}
????@Override
????public?void?trace(String?s)?{
????????log.log(FQCN,?Level.TRACE,?s,?null);
????}
????@Override
????public?void?warn(String?s)?{
????????log.log(FQCN,?Level.WARN,?s,?null);
????}
}
8、裝飾者模式
裝飾模式(Decorator Pattern) :動態地給一個對象增加一些額外的職責(Responsibility),就增加對象功能來說,裝飾模式比生成子類實現更為靈活。其別名也可以稱為包裝器(Wrapper),與適配器模式的別名相同,但它們適用于不同的場合。根據翻譯的不同,裝飾模式也有人稱之為“油漆工模式”,它是一種對象結構型模式。
在mybatis中,緩存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定義。整個體系采用裝飾器設計模式,數據存儲和緩存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久緩存實現,然后通過一系列的裝飾器來對PerpetualCache永久緩存進行緩存策略等方便的控制。如下圖:
用于裝飾PerpetualCache的標準裝飾器共有8個(全部在org.apache.ibatis.cache.decorators包中):
FifoCache:先進先出算法,緩存回收策略
LoggingCache:輸出緩存命中的日志信息
LruCache:最近最少使用算法,緩存回收策略
ScheduledCache:調度緩存,負責定時清空緩存
SerializedCache:緩存序列化和反序列化存儲
SoftCache:基于軟引用實現的緩存管理策略
SynchronizedCache:同步的緩存裝飾器,用于防止多線程并發訪問
WeakCache:基于弱引用實現的緩存管理策略
另外,還有一個特殊的裝飾器TransactionalCache:事務性的緩存
正如大多數持久層框架一樣,mybatis緩存同樣分為一級緩存和二級緩存
二級緩存對象的默認類型為PerpetualCache,如果配置的緩存是默認類型,則mybatis會根據配置自動追加一系列裝飾器。
Cache對象之間的引用順序為:
SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache
9、迭代器模式
迭代器(Iterator)模式,又叫做游標(Cursor)模式。GOF給出的定義為:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。
Java的Iterator就是迭代器模式的接口,只要實現了該接口,就相當于應用了迭代器模式:
比如Mybatis的PropertyTokenizer是property包中的重量級類,該類會被reflection包中其他的類頻繁的引用到。這個類實現了Iterator接口,在使用時經常被用到的是Iterator接口中的hasNext這個函數。
public?class?PropertyTokenizer?implements?Iterator<PropertyTokenizer>?{????private?String?name;????private?String?indexedName;????private?String?index;????private?String?children;????public?PropertyTokenizer(String?fullname)?{????????int?delim?=?fullname.indexOf('.');????????if?(delim?>?-1)?{????????????name?=?fullname.substring(0,?delim);????????????children?=?fullname.substring(delim?+?1);????????}?else?{????????????name?=?fullname;????????????children?=?null;????????}????????indexedName?=?name;????????delim?=?name.indexOf('[');????????if?(delim?>?-1)?{????????????index?=?name.substring(delim?+?1,?name.length()?-?1);????????????name?=?name.substring(0,?delim);????????}????}????public?String?getName()?{????????return?name;????}????public?String?getIndex()?{????????return?index;????}????public?String?getIndexedName()?{????????return?indexedName;????}????public?String?getChildren()?{????????return?children;????}????@Override????public?boolean?hasNext()?{????????return?children?!=?null;????}????@Override????public?PropertyTokenizer?next()?{????????return?new?PropertyTokenizer(children);????}????@Override????public?void?remove()?{????????throw?new?UnsupportedOperationException(????????????????"Remove?is?not?supported,?as?it?has?no?meaning?in?the?context?of?properties.");????}}class?PropertyTokenizer?implements?Iterator<PropertyTokenizer>?{????private?String?name;
????private?String?indexedName;
????private?String?index;
????private?String?children;
????public?PropertyTokenizer(String?fullname)?{
????????int?delim?=?fullname.indexOf('.');
????????if?(delim?>?-1)?{
????????????name?=?fullname.substring(0,?delim);
????????????children?=?fullname.substring(delim?+?1);
????????}?else?{
????????????name?=?fullname;
????????????children?=?null;
????????}
????????indexedName?=?name;
????????delim?=?name.indexOf('[');
????????if?(delim?>?-1)?{
????????????index?=?name.substring(delim?+?1,?name.length()?-?1);
????????????name?=?name.substring(0,?delim);
????????}
????}
????public?String?getName()?{
????????return?name;
????}
????public?String?getIndex()?{
????????return?index;
????}
????public?String?getIndexedName()?{
????????return?indexedName;
????}
????public?String?getChildren()?{
????????return?children;
????}
????@Override
????public?boolean?hasNext()?{
????????return?children?!=?null;
????}
????@Override
????public?PropertyTokenizer?next()?{
????????return?new?PropertyTokenizer(children);
????}
????@Override
????public?void?remove()?{
????????throw?new?UnsupportedOperationException(
????????????????"Remove?is?not?supported,?as?it?has?no?meaning?in?the?context?of?properties.");
????}
}
可以看到,這個類傳入一個字符串到構造函數,然后提供了iterator方法對解析后的子串進行遍歷,是一個很常用的方法類。
參考資料
?圖說設計模式:http://design-patterns.readthedocs.io/zh_CN/latest/index.html?深入淺出Mybatis系列(十)—SQL執行流程分析(源碼篇):http://www.cnblogs.com/dongying/p/4142476.html?設計模式讀書筆記—–組合模式?http://www.cnblogs.com/chenssy/p/3299719.html?Mybatis3.3.x技術內幕(四):五鼠鬧東京之執行器Executor設計原本?http://blog.csdn.net/wagcy/article/details/32963235?mybatis緩存機制詳解(一)——Cache?https://my.oschina.net/lixin91/blog/620068
福利
掃描添加小編微信,備注“姓名+公司職位”,加入【云計算學習交流群】,和志同道合的朋友們共同打卡學習!
推薦閱讀:
聽說,私有云也出新一代了?
搞不懂SDN?那是因為你沒看這個小故事…
華為最強自研 NPU 問世,麒麟 810 “拋棄”寒武紀
北郵通信博士萬字長文,帶你秒懂 4G/5G 區別!
LinkedIn最新報告: 區塊鏈成職位需求增長最快領域, 這些地區對區塊鏈人才渴求度最高……
中文NLP的分詞真有必要嗎?李紀為團隊四項任務評測一探究竟 | ACL 2019
6月技術福利限時免費領
真香,朕在看了! 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的面试官问你MyBatis中有哪些设计模式,把这篇文章发给他的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 平安金管家积分怎么兑换 兑换礼品方式介绍
- 下一篇: 农行住房贷款提交什么材料