0 引言
雖然我們都知道有20多個(gè)設(shè)計(jì)模式,但是大多停留在概念層面坑质,真實(shí)開(kāi)發(fā)中很少遇到合武,Mybatis源碼中使用了大量的設(shè)計(jì)模式,閱讀源碼并觀察設(shè)計(jì)模式在其中的應(yīng)用涡扼,能夠更深入的理解設(shè)計(jì)模式稼跳。Mybatis至少遇到了以下的設(shè)計(jì)模式的使用:
Builder模式,例如SqlSessionFactoryBuilder吃沪、XMLConfigBuilder汤善、XMLMapperBuilder、XMLStatementBuilder票彪、CacheBuilder红淡;工廠模式,例如SqlSessionFactory抹镊、ObjectFactory锉屈、MapperProxyFactory;單例模式垮耳,例如ErrorContext和LogFactory颈渊;代理模式,Mybatis實(shí)現(xiàn)的核心终佛,比如MapperProxy俊嗽、ConnectionLogger,用的jdk的動(dòng)態(tài)代理铃彰;還有executor.loader包使用了cglib或者javassist達(dá)到延遲加載的效果绍豁;組合模式,例如SqlNode和各個(gè)子類(lèi)ChooseSqlNode等牙捉;模板方法模式竹揍,例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和所有的子類(lèi)例如IntegerTypeHandler邪铲;適配器模式芬位,例如Log的Mybatis接口和它對(duì)jdbc、log4j等各種日志框架的適配實(shí)現(xiàn)带到;裝飾者模式昧碉,例如Cache包中的cache.decorators子包中等各個(gè)裝飾者的實(shí)現(xiàn);迭代器模式,例如迭代器模式PropertyTokenizer被饿;
接下來(lái)挨個(gè)模式進(jìn)行解讀四康,先介紹模式自身的知識(shí),然后解讀在Mybatis中怎樣應(yīng)用了該模式狭握。
1?Builder模式
Builder模式的定義是“將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離闪金,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示÷勐”毕泌,它屬于創(chuàng)建類(lèi)模式,一般來(lái)說(shuō)嗅辣,如果一個(gè)對(duì)象的構(gòu)建比較復(fù)雜,超出了構(gòu)造函數(shù)所能包含的范圍挠说,就可以使用工廠模式和Builder模式澡谭,相對(duì)于工廠模式會(huì)產(chǎn)出一個(gè)完整的產(chǎn)品,Builder應(yīng)用于更加復(fù)雜的對(duì)象的構(gòu)建损俭,甚至只會(huì)構(gòu)建產(chǎn)品的一個(gè)部分蛙奖。
在Mybatis環(huán)境的初始化過(guò)程中,SqlSessionFactoryBuilder會(huì)調(diào)用XMLConfigBuilder讀取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件杆兵,構(gòu)建Mybatis運(yùn)行的核心對(duì)象Configuration對(duì)象雁仲,然后將該Configuration對(duì)象作為參數(shù)構(gòu)建一個(gè)SqlSessionFactory對(duì)象。
其中XMLConfigBuilder在構(gòu)建Configuration對(duì)象時(shí)琐脏,也會(huì)調(diào)用XMLMapperBuilder用于讀取*Mapper文件攒砖,而XMLMapperBuilder會(huì)使用XMLStatementBuilder來(lái)讀取和build所有的SQL語(yǔ)句。
在這個(gè)過(guò)程中日裙,有一個(gè)相似的特點(diǎn)吹艇,就是這些Builder會(huì)讀取文件或者配置,然后做大量的XpathParser解析昂拂、配置或語(yǔ)法的解析受神、反射生成對(duì)象、存入結(jié)果緩存等步驟格侯,這么多的工作都不是一個(gè)構(gòu)造函數(shù)所能包括的鼻听,因此大量采用了Builder模式來(lái)解決。
對(duì)于builder的具體類(lèi)联四,方法都大都用build*開(kāi)頭撑碴,比如SqlSessionFactoryBuilder為例,它包含以下方法:
即根據(jù)不同的輸入?yún)?shù)來(lái)構(gòu)建SqlSessionFactory這個(gè)工廠對(duì)象碎连。
2?工廠模式
在Mybatis中比如SqlSessionFactory使用的是工廠模式灰羽,該工廠沒(méi)有那么復(fù)雜的邏輯,是一個(gè)簡(jiǎn)單工廠模式。
簡(jiǎn)單工廠模式(Simple Factory Pattern):又稱(chēng)為靜態(tài)工廠方法(Static Factory Method)模式廉嚼,它屬于類(lèi)創(chuàng)建型模式玫镐。在簡(jiǎn)單工廠模式中,可以根據(jù)參數(shù)的不同返回不同類(lèi)的實(shí)例怠噪。簡(jiǎn)單工廠模式專(zhuān)門(mén)定義一個(gè)類(lèi)來(lái)負(fù)責(zé)創(chuàng)建其他類(lèi)的實(shí)例恐似,被創(chuàng)建的實(shí)例通常都具有共同的父類(lèi)。
SqlSession可以認(rèn)為是一個(gè)Mybatis工作的核心的接口傍念,通過(guò)這個(gè)接口可以執(zhí)行執(zhí)行SQL語(yǔ)句矫夷、獲取Mappers、管理事務(wù)憋槐。類(lèi)似于連接MySQL的Connection對(duì)象双藕。
可以看到,該Factory的openSession方法重載了很多個(gè)阳仔,分別支持autoCommit忧陪、Executor、Transaction等參數(shù)的輸入近范,來(lái)構(gòu)建核心的SqlSession對(duì)象嘶摊。
在DefaultSqlSessionFactory的默認(rèn)工廠實(shí)現(xiàn)里,有一個(gè)方法可以看出工廠怎么產(chǎn)出一個(gè)產(chǎn)品:
privateSqlSessionopenSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,booleanautoCommit){Transaction tx =null;try{finalEnvironment environment = configuration.getEnvironment();finalTransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);? ? tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);finalExecutor executor = configuration.newExecutor(tx, execType);returnnewDefaultSqlSession(configuration, executor, autoCommit);}catch(Exception e) {closeTransaction(tx);// may have fetched a connection so lets call// close()throwExceptionFactory.wrapException("Error opening session.? Cause: "+ e, e);}finally{? ? ErrorContext.instance().reset();? } }
這是一個(gè)openSession調(diào)用的底層方法评矩,該方法先從configuration讀取對(duì)應(yīng)的環(huán)境配置叶堆,然后初始化TransactionFactory獲得一個(gè)Transaction對(duì)象,然后通過(guò)Transaction獲取一個(gè)Executor對(duì)象斥杜,最后通過(guò)configuration虱颗、Executor、是否autoCommit三個(gè)參數(shù)構(gòu)建了SqlSession果录。
在這里其實(shí)也可以看到端倪上枕,SqlSession的執(zhí)行,其實(shí)是委托給對(duì)應(yīng)的Executor來(lái)進(jìn)行的弱恒。
而對(duì)于LogFactory辨萍,它的實(shí)現(xiàn)代碼:
publicfinalclassLogFactory{privatestaticConstructor<?extends Log> logConstructor;privateLogFactory() {// disable construction }publicstaticLog getLog(ClassaClass){returngetLog(aClass.getName()); }
這里有個(gè)特別的地方,是Log變量的的類(lèi)型是Constructor<? extends Log>返弹,也就是說(shuō)該工廠生產(chǎn)的不只是一個(gè)產(chǎn)品锈玉,而是具有Log公共接口的一系列產(chǎn)品,比如Log4jImpl义起、Slf4jImpl等很多具體的Log裁替。
3?單例模式
單例模式(Singleton Pattern):?jiǎn)卫J酱_保某一個(gè)類(lèi)只有一個(gè)實(shí)例毛俏,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,這個(gè)類(lèi)稱(chēng)為單例類(lèi),它提供全局訪(fǎng)問(wèn)的方法马僻。
單例模式的要點(diǎn)有三個(gè):一是某個(gè)類(lèi)只能有一個(gè)實(shí)例;二是它必須自行創(chuàng)建這個(gè)實(shí)例;三是它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。單例模式是一種對(duì)象創(chuàng)建型模式床估。單例模式又名單件模式或單態(tài)模式。
在Mybatis中有兩個(gè)地方用到單例模式诱渤,ErrorContext和LogFactory丐巫,其中ErrorContext是用在每個(gè)線(xiàn)程范圍內(nèi)的單例,用于記錄該線(xiàn)程的執(zhí)行環(huán)境錯(cuò)誤信息勺美,而LogFactory則是提供給整個(gè)Mybatis使用的日志工廠递胧,用于獲得針對(duì)項(xiàng)目配置好的日志對(duì)象。
ErrorContext的單例實(shí)現(xiàn)代碼:
publicclassErrorContext{privatestaticfinal ThreadLocal LOCAL =newThreadLocal();privateErrorContext(){ }publicstaticErrorContextinstance(){ErrorContext context = LOCAL.get();if(context ==null) {context =newErrorContext();LOCAL.set(context);? }returncontext; }
構(gòu)造函數(shù)是private修飾赡茸,具有一個(gè)static的局部instance變量和一個(gè)獲取instance變量的方法缎脾,在獲取實(shí)例的方法中,先判斷是否為空如果是的話(huà)就先創(chuàng)建占卧,然后返回構(gòu)造好的對(duì)象赊锚。
只是這里有個(gè)有趣的地方是,LOCAL的靜態(tài)實(shí)例變量使用了ThreadLocal修飾屉栓,也就是說(shuō)它屬于每個(gè)線(xiàn)程各自的數(shù)據(jù),而在instance()方法中耸袜,先獲取本線(xiàn)程的該實(shí)例友多,如果沒(méi)有就創(chuàng)建該線(xiàn)程獨(dú)有的ErrorContext。
4?代理模式
代理模式可以認(rèn)為是Mybatis的核心使用的模式堤框,正是由于這個(gè)模式域滥,我們只需要編寫(xiě)Mapper.java接口,不需要實(shí)現(xiàn)蜈抓,由Mybatis后臺(tái)幫我們完成具體SQL的執(zhí)行启绰。
代理模式(Proxy Pattern)?:給某一個(gè)對(duì)象提供一個(gè)代?理,并由代理對(duì)象控制對(duì)原對(duì)象的引用沟使。代理模式的英?文叫做Proxy或Surrogate委可,它是一種對(duì)象結(jié)構(gòu)型模式。
代理模式包含如下角色:
Subject: 抽象主題角色Proxy: 代理主題角色RealSubject: 真實(shí)主題角色
這里有兩個(gè)步驟腊嗡,第一個(gè)是提前創(chuàng)建一個(gè)Proxy着倾,第二個(gè)是使用的時(shí)候會(huì)自動(dòng)請(qǐng)求Proxy,然后由Proxy來(lái)執(zhí)行具體事務(wù)燕少;
當(dāng)我們使用Configuration的getMapper方法時(shí)卡者,會(huì)調(diào)用mapperRegistry.getMapper方法,而該方法又會(huì)調(diào)用mapperProxyFactory.newInstance(sqlSession)來(lái)生成一個(gè)具體的代理:
/***@authorLasse Voss*/publicclassMapperProxyFactory{privatefinalClass mapperInterface;privatefinalMap methodCache =newConcurrentHashMap();publicMapperProxyFactory(Class<T> mapperInterface){this.mapperInterface = mapperInterface; }publicClassgetMapperInterface(){returnmapperInterface; }publicMapgetMethodCache(){returnmethodCache; }@SuppressWarnings("unchecked")protectedTnewInstance(MapperProxy<T> mapperProxy){return(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),newClass[] { mapperInterface },? ? ? mapperProxy); }publicTnewInstance(SqlSession sqlSession){finalMapperProxy mapperProxy =newMapperProxy(sqlSession, mapperInterface, methodCache);returnnewInstance(mapperProxy); }}
在這里客们,先通過(guò)T newInstance(SqlSession sqlSession)方法會(huì)得到一個(gè)MapperProxy對(duì)象崇决,然后調(diào)用T newInstance(MapperProxy<T> mapperProxy)生成代理對(duì)象然后返回材诽。
而查看MapperProxy的代碼,可以看到如下內(nèi)容:
publicclassMapperProxyimplementsInvocationHandler,Serializable {@OverridepublicObject invoke(Object proxy, Method method, Object[] args) throws Throwable {try{if(Object.class.equals(method.getDeclaringClass())) {returnmethod.invoke(this, args);}elseif(isDefaultMethod(method)) {returninvokeDefaultMethod(proxy, method, args);? ? }}catch(Throwable t) {throwExceptionUtil.unwrapThrowable(t);? }finalMapperMethod mapperMethod = cachedMapperMethod(method);returnmapperMethod.execute(sqlSession, args); }
非常典型的恒傻,該MapperProxy類(lèi)實(shí)現(xiàn)了InvocationHandler接口脸侥,并且實(shí)現(xiàn)了該接口的invoke方法。
通過(guò)這種方式碌冶,我們只需要編寫(xiě)Mapper.java接口類(lèi)湿痢,當(dāng)真正執(zhí)行一個(gè)Mapper接口的時(shí)候,就會(huì)轉(zhuǎn)發(fā)給MapperProxy.invoke方法扑庞,而該方法則會(huì)調(diào)用后續(xù)的sqlSession.cud>executor.execute>prepareStatement等一系列方法譬重,完成SQL的執(zhí)行和返回。
5?組合模式
組合模式組合多個(gè)對(duì)象形成樹(shù)形結(jié)構(gòu)以表示“整體-部分”的結(jié)構(gòu)層次罐氨。
組合模式對(duì)單個(gè)對(duì)象(葉子對(duì)象)和組合對(duì)象(組合對(duì)象)具有一致性臀规,它將對(duì)象組織到樹(shù)結(jié)構(gòu)中,可以用來(lái)描述整體與部分的關(guān)系栅隐。同時(shí)它也模糊了簡(jiǎn)單元素(葉子對(duì)象)和復(fù)雜元素(容器對(duì)象)的概念塔嬉,使得客戶(hù)能夠像處理簡(jiǎn)單元素一樣來(lái)處理復(fù)雜元素,從而使客戶(hù)程序能夠與復(fù)雜元素的內(nèi)部結(jié)構(gòu)解耦租悄。
在使用組合模式中需要注意一點(diǎn)也是組合模式最關(guān)鍵的地方:葉子對(duì)象和組合對(duì)象實(shí)現(xiàn)相同的接口谨究。這就是組合模式能夠?qū)⑷~子節(jié)點(diǎn)和對(duì)象節(jié)點(diǎn)進(jìn)行一致處理的原因。
Mybatis支持動(dòng)態(tài)SQL的強(qiáng)大功能泣棋,比如下面的這個(gè)SQL:
? UPDATE usersname =#{name}, age =#{age}, birthday =#{birthday}? </trim>whereid =${id}</update>
在這里面使用到了trim胶哲、if等動(dòng)態(tài)元素,可以根據(jù)條件來(lái)生成不同情況下的SQL潭辈;
在DynamicSqlSource.getBoundSql方法里鸯屿,調(diào)用了rootSqlNode.apply(context)方法,apply方法是所有的動(dòng)態(tài)節(jié)點(diǎn)都實(shí)現(xiàn)的接口:
publicinterfaceSqlNode{booleanapply(DynamicContext context);}
對(duì)于實(shí)現(xiàn)該SqlSource接口的所有節(jié)點(diǎn)把敢,就是整個(gè)組合模式樹(shù)的各個(gè)節(jié)點(diǎn):
組合模式的簡(jiǎn)單之處在于寄摆,所有的子節(jié)點(diǎn)都是同一類(lèi)節(jié)點(diǎn),可以遞歸的向下執(zhí)行修赞,比如對(duì)于TextSqlNode婶恼,因?yàn)樗亲畹讓拥娜~子節(jié)點(diǎn),所以直接將對(duì)應(yīng)的內(nèi)容append到SQL語(yǔ)句中:
@Overridepublicbooleanapply(DynamicContext context){GenericTokenParser parser = createParser(newBindingTokenParser(context, injectionFilter));? context.appendSql(parser.parse(text));returntrue; }
但是對(duì)于IfSqlNode柏副,就需要先做判斷熙尉,如果判斷通過(guò),仍然會(huì)調(diào)用子元素的SqlNode搓扯,即contents.apply方法检痰,實(shí)現(xiàn)遞歸的解析。
6?模板方法模式
模板方法模式是所有模式中最為常見(jiàn)的幾個(gè)模式之一锨推,是基于繼承的代碼復(fù)用的基本技術(shù)铅歼。關(guān)注Java技術(shù)棧微信公眾號(hào)公壤,在后臺(tái)回復(fù)關(guān)鍵字:架構(gòu),可以獲取更多棧長(zhǎng)整理的架構(gòu)和設(shè)計(jì)模式干貨椎椰。
模板方法模式需要開(kāi)發(fā)抽象類(lèi)和具體子類(lèi)的設(shè)計(jì)師之間的協(xié)作厦幅。一個(gè)設(shè)計(jì)師負(fù)責(zé)給出一個(gè)算法的輪廓和骨架,另一些設(shè)計(jì)師則負(fù)責(zé)給出這個(gè)算法的各個(gè)邏輯步驟慨飘。代表這些具體邏輯步驟的方法稱(chēng)做基本方法(primitive method)确憨;而將這些基本方法匯總起來(lái)的方法叫做模板方法(template method),這個(gè)設(shè)計(jì)模式的名字就是從此而來(lái)瓤的。
模板類(lèi)定義一個(gè)操作中的算法的骨架休弃,而將一些步驟延遲到子類(lèi)中。使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟圈膏。
在Mybatis中塔猾,sqlSession的SQL執(zhí)行,都是委托給Executor實(shí)現(xiàn)的稽坤,Executor包含以下結(jié)構(gòu):
其中的BaseExecutor就采用了模板方法模式丈甸,它實(shí)現(xiàn)了大部分的SQL執(zhí)行邏輯,然后把以下幾個(gè)方法交給子類(lèi)定制化完成:
protectedabstractintdoUpdate(MappedStatement ms, Object parameter)throwsSQLException;protectedabstractListdoFlushStatements(booleanisRollback)throwsSQLException;protectedabstractListdoQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,? ? ResultHandler resultHandler, BoundSql boundSql)throwsSQLException;
該模板方法類(lèi)有幾個(gè)子類(lèi)的具體實(shí)現(xiàn)尿褪,使用了不同的策略:
簡(jiǎn)單SimpleExecutor:每執(zhí)行一次update或select睦擂,就開(kāi)啟一個(gè)Statement對(duì)象,用完立刻關(guān)閉Statement對(duì)象杖玲。(可以是Statement或PrepareStatement對(duì)象)
重用ReuseExecutor:執(zhí)行update或select祈匙,以sql作為key查找Statement對(duì)象,存在就使用天揖,不存在就創(chuàng)建,用完后跪帝,不關(guān)閉Statement對(duì)象今膊,而是放置于Map<String, Statement>內(nèi),供下一次使用伞剑。(可以是Statement或PrepareStatement對(duì)象)
批量BatchExecutor:執(zhí)行update(沒(méi)有select斑唬,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch())黎泣,等待統(tǒng)一執(zhí)行(executeBatch())恕刘,它緩存了多個(gè)Statement對(duì)象,每個(gè)Statement對(duì)象都是addBatch()完畢后抒倚,等待逐一執(zhí)行executeBatch()批處理的褐着;BatchExecutor相當(dāng)于維護(hù)了多個(gè)桶,每個(gè)桶里都裝了很多屬于自己的SQL托呕,就像蘋(píng)果藍(lán)里裝了很多蘋(píng)果含蓉,番茄藍(lán)里裝了很多番茄频敛,最后,再統(tǒng)一倒進(jìn)倉(cāng)庫(kù)馅扣。(可以是Statement或PrepareStatement對(duì)象)
比如在SimpleExecutor中這樣實(shí)現(xiàn)update方法:
@OverridepublicintdoUpdate(MappedStatement ms, Object parameter)throwsSQLException{Statement stmt =null;try{? ? Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT,null,null);? ? stmt = prepareStatement(handler, ms.getStatementLog());returnhandler.update(stmt);}finally{? ? closeStatement(stmt);? } }
7?適配器模式
適配器模式(Adapter Pattern)?:將一個(gè)接口轉(zhuǎn)換成客戶(hù)希望的另一個(gè)接口斟赚,適配器模式使接口不兼容的那些類(lèi)可以一起工作,其別名為包裝器(Wrapper)差油。適配器模式既可以作為類(lèi)結(jié)構(gòu)型模式拗军,也可以作為對(duì)象結(jié)構(gòu)型模式。
在Mybatsi的logging包中蓄喇,有一個(gè)Log接口:
/***@authorClinton Begin*/publicinterfaceLog{booleanisDebugEnabled();booleanisTraceEnabled();voiderror(String s, Throwable e);voiderror(String s);voiddebug(String s);voidtrace(String s);voidwarn(String s);}
該接口定義了Mybatis直接使用的日志方法发侵,而Log接口具體由誰(shuí)來(lái)實(shí)現(xiàn)呢?Mybatis提供了多種日志框架的實(shí)現(xiàn)公罕,這些實(shí)現(xiàn)都匹配這個(gè)Log接口所定義的接口方法器紧,最終實(shí)現(xiàn)了所有外部日志框架到Mybatis日志包的適配:
比如對(duì)于Log4jImpl的實(shí)現(xiàn)來(lái)說(shuō),該實(shí)現(xiàn)持有了org.apache.log4j.Logger的實(shí)例楼眷,然后所有的日志方法铲汪,均委托該實(shí)例來(lái)實(shí)現(xiàn)。
publicclassLog4jImplimplementsLog{privatestaticfinalString FQCN = Log4jImpl.class.getName();privateLogger log;publicLog4jImpl(String clazz){? log = Logger.getLogger(clazz); }@OverridepublicbooleanisDebugEnabled(){returnlog.isDebugEnabled(); }@OverridepublicbooleanisTraceEnabled(){returnlog.isTraceEnabled(); }@Overridepublicvoiderror(String s, Throwable e){? log.log(FQCN, Level.ERROR, s, e); }@Overridepublicvoiderror(String s){log.log(FQCN, Level.ERROR, s,null); }@Overridepublicvoiddebug(String s){log.log(FQCN, Level.DEBUG, s,null); }@Overridepublicvoidtrace(String s){log.log(FQCN, Level.TRACE, s,null); }@Overridepublicvoidwarn(String s){log.log(FQCN, Level.WARN, s,null); }}
8 裝飾者模式
裝飾模式(Decorator Pattern)?:動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的職責(zé)(Responsibility)罐柳,就增加對(duì)象功能來(lái)說(shuō)掌腰,裝飾模式比生成子類(lèi)實(shí)現(xiàn)更為靈活。其別名也可以稱(chēng)為包裝器(Wrapper)张吉,與適配器模式的別名相同齿梁,但它們適用于不同的場(chǎng)合。根據(jù)翻譯的不同肮蛹,裝飾模式也有人稱(chēng)之為“油漆工模式”勺择,它是一種對(duì)象結(jié)構(gòu)型模式。?
在mybatis中伦忠,緩存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定義省核。關(guān)注Java技術(shù)棧微信公眾號(hào),在后臺(tái)回復(fù)關(guān)鍵字:架構(gòu)昆码,可以獲取更多棧長(zhǎng)整理的架構(gòu)和設(shè)計(jì)模式干貨气忠。
整個(gè)體系采用裝飾器設(shè)計(jì)模式,數(shù)據(jù)存儲(chǔ)和緩存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久緩存實(shí)現(xiàn)赋咽,然后通過(guò)一系列的裝飾器來(lái)對(duì)PerpetualCache永久緩存進(jìn)行緩存策略等方便的控制旧噪。如下圖:
用于裝飾PerpetualCache的標(biāo)準(zhǔn)裝飾器共有8個(gè)(全部在org.apache.ibatis.cache.decorators包中):
FifoCache:先進(jìn)先出算法,緩存回收策略L(fǎng)oggingCache:輸出緩存命中的日志信息LruCache:最近最少使用算法脓匿,緩存回收策略ScheduledCache:調(diào)度緩存淘钟,負(fù)責(zé)定時(shí)清空緩存SerializedCache:緩存序列化和反序列化存儲(chǔ)SoftCache:基于軟引用實(shí)現(xiàn)的緩存管理策略SynchronizedCache:同步的緩存裝飾器,用于防止多線(xiàn)程并發(fā)訪(fǎng)問(wèn)WeakCache:基于弱引用實(shí)現(xiàn)的緩存管理策略
另外陪毡,還有一個(gè)特殊的裝飾器TransactionalCache:事務(wù)性的緩存
正如大多數(shù)持久層框架一樣日月,mybatis緩存同樣分為一級(jí)緩存和二級(jí)緩存
一級(jí)緩存袱瓮,又叫本地緩存,是PerpetualCache類(lèi)型的永久緩存爱咬,保存在執(zhí)行器中(BaseExecutor)尺借,而執(zhí)行器又在SqlSession(DefaultSqlSession)中,所以一級(jí)緩存的生命周期與SqlSession是相同的精拟。
二級(jí)緩存燎斩,又叫自定義緩存,實(shí)現(xiàn)了Cache接口的類(lèi)都可以作為二級(jí)緩存蜂绎,所以可配置如encache等的第三方緩存栅表。二級(jí)緩存以namespace名稱(chēng)空間為其唯一標(biāo)識(shí),被保存在Configuration核心配置對(duì)象中师枣。二級(jí)緩存對(duì)象的默認(rèn)類(lèi)型為PerpetualCache怪瓶,如果配置的緩存是默認(rèn)類(lèi)型,則mybatis會(huì)根據(jù)配置自動(dòng)追加一系列裝飾器践美。
Cache對(duì)象之間的引用順序?yàn)椋?/p>
SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache
9 迭代器模式
迭代器(Iterator)模式洗贰,又叫做游標(biāo)(Cursor)模式。GOF給出的定義為:提供一種方法訪(fǎng)問(wèn)一個(gè)容器(container)對(duì)象中各個(gè)元素陨倡,而又不需暴露該對(duì)象的內(nèi)部細(xì)節(jié)敛滋。
Java的Iterator就是迭代器模式的接口,只要實(shí)現(xiàn)了該接口兴革,就相當(dāng)于應(yīng)用了迭代器模式:
比如Mybatis的PropertyTokenizer是property包中的重量級(jí)類(lèi)绎晃,該類(lèi)會(huì)被reflection包中其他的類(lèi)頻繁的引用到。這個(gè)類(lèi)實(shí)現(xiàn)了Iterator接口杂曲,在使用時(shí)經(jīng)常被用到的是Iterator接口中的hasNext這個(gè)函數(shù)庶艾。
publicclassPropertyTokenizerimplementsIterator{privateString name;privateString indexedName;privateString index;privateString children;publicPropertyTokenizer(String fullname){intdelim = 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);? } }publicStringgetName(){returnname; }publicStringgetIndex(){returnindex; }publicStringgetIndexedName(){returnindexedName; }publicStringgetChildren(){returnchildren; }@OverridepublicbooleanhasNext(){returnchildren !=null; }@OverridepublicPropertyTokenizernext(){returnnewPropertyTokenizer(children); }@Overridepublicvoidremove(){thrownewUnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties."); }}
可以看到,這個(gè)類(lèi)傳入一個(gè)字符串到構(gòu)造函數(shù)擎勘,然后提供了iterator方法對(duì)解析后的子串進(jìn)行遍歷咱揍,是一個(gè)很常用的方法類(lèi)。
參考文章:https://mp.weixin.qq.com/s/2p_gxBuaCSGgrq9aKoGEIg