<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Mybatis源碼解讀-mybatis使用的9種設(shè)計(jì)模式總結(jié)

          共 3140字,需瀏覽 7分鐘

           ·

          2022-05-22 08:18

          關(guān)注Java學(xué)習(xí)之道一起成長(zhǎng),一起學(xué)習(xí)~

          來源: crazyant.net/2022.html

          Part1前言

          雖然我們都知道有26個(gè)設(shè)計(jì)模式,但是大多停留在概念層面,真實(shí)開發(fā)中很少遇到,Mybatis源碼中使用了大量的設(shè)計(jì)模式,閱讀源碼并觀察設(shè)計(jì)模式在其中的應(yīng)用,能夠更深入的理解設(shè)計(jì)模式。

          Mybatis至少遇到了以下的設(shè)計(jì)模式的使用:

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

          接下來挨個(gè)模式進(jìn)行解讀,先介紹模式自身的知識(shí),然后解讀在Mybatis中怎樣應(yīng)用了該模式。

          Part2Builder模式

          Builder模式的定義是“將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。”,它屬于創(chuàng)建類模式,一般來說,如果一個(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)境的初始化過程中,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來讀取和build所有的SQL語(yǔ)句。

          在這個(gè)過程中,有一個(gè)相似的特點(diǎn),就是這些Builder會(huì)讀取文件或者配置,然后做大量的XpathParser解析、配置或語(yǔ)法的解析、反射生成對(duì)象、存入結(jié)果緩存等步驟,這么多的工作都不是一個(gè)構(gòu)造函數(shù)所能包括的,因此大量采用了Builder模式來解決。

          歡迎關(guān)注公眾號(hào)"Java學(xué)習(xí)之道",查看更多干貨!

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

          即根據(jù)不同的輸入?yún)?shù)來構(gòu)建SqlSessionFactory這個(gè)工廠對(duì)象。

          Part3工廠模式

          在Mybatis中比如SqlSessionFactory使用的是工廠模式,該工廠沒有那么復(fù)雜的邏輯,是一個(gè)簡(jiǎn)單工廠模式。

          簡(jiǎn)單工廠模式(Simple Factory Pattern):又稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式。在簡(jiǎn)單工廠模式中,可以根據(jù)參數(shù)的不同返回不同類的實(shí)例。簡(jiǎn)單工廠模式專門定義一個(gè)類來負(fù)責(zé)創(chuàng)建其他類的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。

          SqlSession可以認(rèn)為是一個(gè)Mybatis工作的核心的接口,通過這個(gè)接口可以執(zhí)行執(zhí)行SQL語(yǔ)句、獲取Mappers、管理事務(wù)。類似于連接MySQL的Connection對(duì)象。

          可以看到,該Factory的openSession方法重載了很多個(gè),分別支持autoCommit、Executor、Transaction等參數(shù)的輸入,來構(gòu)建核心的SqlSession對(duì)象。

          在DefaultSqlSessionFactory的默認(rèn)工廠實(shí)現(xiàn)里,有一個(gè)方法可以看出工廠怎么產(chǎn)出一個(gè)產(chǎn)品:

          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();
          ????}
          }

          這是一個(gè)openSession調(diào)用的底層方法,該方法先從configuration讀取對(duì)應(yīng)的環(huán)境配置,然后初始化TransactionFactory獲得一個(gè)Transaction對(duì)象,然后通過Transaction獲取一個(gè)Executor對(duì)象,最后通過configuration、Executor、是否autoCommit三個(gè)參數(shù)構(gòu)建了SqlSession。

          在這里其實(shí)也可以看到端倪,SqlSession的執(zhí)行,其實(shí)是委托給對(duì)應(yīng)的Executor來進(jìn)行的。

          而對(duì)于LogFactory,它的實(shí)現(xiàn)代碼:

          public?final?class?LogFactory?{
          ?private?static?Constructor?logConstructor;
          ?private?LogFactory()?{
          ??//?disable?construction
          ?}
          ?public?static?Log?getLog(Class?aClass)?{
          ??return?getLog(aClass.getName());
          ?}
          }

          這里有個(gè)特別的地方,是Log變量的的類型是Constructorextends Log>,也就是說該工廠生產(chǎn)的不只是一個(gè)產(chǎn)品,而是具有Log公共接口的一系列產(chǎn)品,比如Log4jImpl、Slf4jImpl等很多具體的Log。

          Part4單例模式

          單例模式(Singleton Pattern):?jiǎn)卫J酱_保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例,這個(gè)類稱為單例類,它提供全局訪問的方法。

          單例模式的要點(diǎn)有三個(gè):一是某個(gè)類只能有一個(gè)實(shí)例;二是它必須自行創(chuàng)建這個(gè)實(shí)例;三是它必須自行向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。單例模式是一種對(duì)象創(chuàng)建型模式。單例模式又名單件模式或單態(tài)模式。

          在Mybatis中有兩個(gè)地方用到單例模式,ErrorContext和LogFactory,其中ErrorContext是用在每個(gè)線程范圍內(nèi)的單例,用于記錄該線程的執(zhí)行環(huán)境錯(cuò)誤信息,而LogFactory則是提供給整個(gè)Mybatis使用的日志工廠,用于獲得針對(duì)項(xiàng)目配置好的日志對(duì)象。

          ErrorContext的單例實(shí)現(xiàn)代碼:

          public?class?ErrorContext?{
          ?private?static?final?ThreadLocal?LOCAL?=?new?ThreadLocal();
          ?private?ErrorContext()?{
          ?}
          ?public?static?ErrorContext?instance()?{
          ??ErrorContext?context?=?LOCAL.get();
          ??if?(context?==?null)?{
          ???context?=?new?ErrorContext();
          ???LOCAL.set(context);
          ??}
          ??return?context;
          ?}
          }

          構(gòu)造函數(shù)是private修飾,具有一個(gè)static的局部instance變量和一個(gè)獲取instance變量的方法,在獲取實(shí)例的方法中,先判斷是否為空如果是的話就先創(chuàng)建,然后返回構(gòu)造好的對(duì)象。

          歡迎關(guān)注公眾號(hào)"Java學(xué)習(xí)之道",查看更多干貨!

          只是這里有個(gè)有趣的地方是,LOCAL的靜態(tài)實(shí)例變量使用了ThreadLocal修飾,也就是說它屬于每個(gè)線程各自的數(shù)據(jù),而在instance()方法中,先獲取本線程的該實(shí)例,如果沒有就創(chuàng)建該線程獨(dú)有的ErrorContext。

          Part5代理模式

          代理模式可以認(rèn)為是Mybatis的核心使用的模式,正是由于這個(gè)模式,我們只需要編寫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來執(zhí)行具體事務(wù);

          當(dāng)我們使用Configuration的getMapper方法時(shí),會(huì)調(diào)用mapperRegistry**.getMapper方法,而該方法又會(huì)調(diào)用mapperProxyFactory.newInstance(sqlSession)來生成一個(gè)具體的代理:**

          /**
          ?*?@author?Lasse?Voss
          ?*/

          public?class?MapperProxyFactory<T>?{
          ?private?final?Class?mapperInterface;
          ?private?final?Map?methodCache?=?new?ConcurrentHashMap();
          ?public?MapperProxyFactory(Class?mapperInterface)?{
          ??this.mapperInterface?=?mapperInterface;
          ?}
          ?public?Class?getMapperInterface()?{
          ??return?mapperInterface;
          ?}
          ?public?Map?getMethodCache()?{
          ??return?methodCache;
          ?}
          ?@SuppressWarnings("unchecked")
          ?protected?T?newInstance(MapperProxy?mapperProxy)?{
          ??return?(T)?Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[]?{?mapperInterface?},
          ????mapperProxy);
          ?}
          ?public?T?newInstance(SqlSession?sqlSession)?{
          ??final?MapperProxy?mapperProxy?=?new?MapperProxy(sqlSession,?mapperInterface,?methodCache);
          ??return?newInstance(mapperProxy);
          ?}
          }

          在這里,先通過T newInstance(SqlSession sqlSession)方法會(huì)得到一個(gè)MapperProxy對(duì)象,然后調(diào)用T newInstance(MapperProxymapperProxy)生成代理對(duì)象然后返回。

          而查看MapperProxy的代碼,可以看到如下內(nèi)容:

          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);
          ?}
          }

          非常典型的,該MapperProxy類實(shí)現(xiàn)了InvocationHandler接口,并且實(shí)現(xiàn)了該接口的invoke方法。

          通過這種方式,我們只需要編寫Mapper.java接口類,當(dāng)真正執(zhí)行一個(gè)Mapper接口的時(shí)候,就會(huì)轉(zhuǎn)發(fā)給MapperProxy.invoke方法,而該方法則會(huì)調(diào)用后續(xù)的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的執(zhí)行和返回。

          Part6組合模式

          組合模式組合多個(gè)對(duì)象形成樹形結(jié)構(gòu)以表示“整體-部分”的結(jié)構(gòu)層次。

          組合模式對(duì)單個(gè)對(duì)象(葉子對(duì)象)和組合對(duì)象(組合對(duì)象)具有一致性,它將對(duì)象組織到樹結(jié)構(gòu)中,可以用來描述整體與部分的關(guān)系。同時(shí)它也模糊了簡(jiǎn)單元素(葉子對(duì)象)和復(fù)雜元素(容器對(duì)象)的概念,使得客戶能夠像處理簡(jiǎn)單元素一樣來處理復(fù)雜元素,從而使客戶程序能夠與復(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?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>

          在這里面使用到了trim、if等動(dòng)態(tài)元素,可以根據(jù)條件來生成不同情況下的SQL;

          在DynamicSqlSource.getBoundSql方法里,調(diào)用了rootSqlNode.apply(context)方法,apply方法是所有的動(dòng)態(tài)節(jié)點(diǎn)都實(shí)現(xiàn)的接口:

          public?interface?SqlNode?{
          ?boolean?apply(DynamicContext?context);
          }

          對(duì)于實(shí)現(xiàn)該SqlSource接口的所有節(jié)點(diǎn),就是整個(gè)組合模式樹的各個(gè)節(jié)點(diǎn):

          組合模式的簡(jiǎn)單之處在于,所有的子節(jié)點(diǎn)都是同一類節(jié)點(diǎn),可以遞歸的向下執(zhí)行,比如對(duì)于TextSqlNode,因?yàn)樗亲畹讓拥娜~子節(jié)點(diǎn),所以直接將對(duì)應(yīng)的內(nèi)容append到SQL語(yǔ)句中:

          @Override
          public?boolean?apply(DynamicContext?context)?{
          ????GenericTokenParser?parser?=?createParser(new?BindingTokenParser(context,?injectionFilter));
          ????context.appendSql(parser.parse(text));
          ????return?true;
          }

          但是對(duì)于IfSqlNode,就需要先做判斷,如果判斷通過,仍然會(huì)調(diào)用子元素的SqlNode,即contents.apply方法,實(shí)現(xiàn)遞歸的解析。

          @Override
          public?boolean?apply(DynamicContext?context)?{
          ????if?(evaluator.evaluateBoolean(test,?context.getBindings()))?{
          ????????contents.apply(context);
          ????????return?true;
          ????}
          ????return?false;
          }

          Part7模板方法模式

          模板方法模式是所有模式中最為常見的幾個(gè)模式之一,是基于繼承的代碼復(fù)用的基本技術(shù)。

          模板方法模式需要開發(fā)抽象類和具體子類的設(shè)計(jì)師之間的協(xié)作。一個(gè)設(shè)計(jì)師負(fù)責(zé)給出一個(gè)算法的輪廓和骨架,另一些設(shè)計(jì)師則負(fù)責(zé)給出這個(gè)算法的各個(gè)邏輯步驟。代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本方法匯總起來的方法叫做模板方法(template method),這個(gè)設(shè)計(jì)模式的名字就是從此而來。

          歡迎關(guān)注公眾號(hào)"Java學(xué)習(xí)之道",查看更多干貨!

          模板類定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類中。使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。

          在Mybatis中,sqlSession的SQL執(zhí)行,都是委托給Executor實(shí)現(xiàn)的,Executor包含以下結(jié)構(gòu):

          其中的BaseExecutor就采用了模板方法模式,它實(shí)現(xiàn)了大部分的SQL執(zhí)行邏輯,然后把以下幾個(gè)方法交給子類定制化完成:

          protected?abstract?int?doUpdate(MappedStatement?ms,?Object?parameter)?throws?SQLException;
          protected?abstract?List?doFlushStatements(boolean?isRollback)?throws?SQLException;
          protected?abstract??List?doQuery(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,
          ????????ResultHandler?resultHandler,?BoundSql?boundSql)
          ?throws?SQLException
          ;

          該模板方法類有幾個(gè)子類的具體實(shí)現(xiàn),使用了不同的策略:

          • 簡(jiǎn)單SimpleExecutor:每執(zhí)行一次update或select,就開啟一個(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內(nèi),供下一次使用。(可以是Statement或PrepareStatement對(duì)象)
          • 批量BatchExecutor:執(zhí)行update(沒有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,就像蘋果藍(lán)里裝了很多蘋果,番茄藍(lán)里裝了很多番茄,最后,再統(tǒng)一倒進(jìn)倉(cāng)庫(kù)。(可以是Statement或PrepareStatement對(duì)象)

          比如在SimpleExecutor中這樣實(shí)現(xiàn)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);
          ????}
          }

          Part8適配器模式

          適配器模式(Adapter Pattern) :將一個(gè)接口轉(zhuǎn)換成客戶希望的另一個(gè)接口,適配器模式使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對(duì)象結(jié)構(gòu)型模式。

          在Mybatsi的logging包中,有一個(gè)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);
          }

          該接口定義了Mybatis直接使用的日志方法,而Log接口具體由誰(shuí)來實(shí)現(xiàn)呢?Mybatis提供了多種日志框架的實(shí)現(xiàn),這些實(shí)現(xiàn)都匹配這個(gè)Log接口所定義的接口方法,最終實(shí)現(xiàn)了所有外部日志框架到Mybatis日志包的適配:


          mybatis_logging_impls.png)


          比如對(duì)于Log4jImpl的實(shí)現(xiàn)來說,該實(shí)現(xiàn)持有了org.apache.log4j.Logger的實(shí)例,然后所有的日志方法,均委托該實(shí)例來實(shí)現(xiàn)。

          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);
          ?}
          }

          Part9裝飾者模式

          裝飾模式(Decorator Pattern) :動(dòng)態(tài)地給一個(gè)對(duì)象增加一些額外的職責(zé)(Responsibility),就增加對(duì)象功能來說,裝飾模式比生成子類實(shí)現(xiàn)更為靈活。其別名也可以稱為包裝器(Wrapper),與適配器模式的別名相同,但它們適用于不同的場(chǎng)合。根據(jù)翻譯的不同,裝飾模式也有人稱之為“油漆工模式”,它是一種對(duì)象結(jié)構(gòu)型模式。

          在mybatis中,緩存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定義。整個(gè)體系采用裝飾器設(shè)計(jì)模式,數(shù)據(jù)存儲(chǔ)和緩存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久緩存實(shí)現(xiàn),然后通過一系列的裝飾器來對(duì)PerpetualCache永久緩存進(jìn)行緩存策略等方便的控制。如下圖:

          用于裝飾PerpetualCache的標(biāo)準(zhǔn)裝飾器共有8個(gè)(全部在org.apache.ibatis.cache.decorators包中):

          1. FifoCache:先進(jìn)先出算法,緩存回收策略
          2. LoggingCache:輸出緩存命中的日志信息
          3. LruCache:最近最少使用算法,緩存回收策略
          4. ScheduledCache:調(diào)度緩存,負(fù)責(zé)定時(shí)清空緩存
          5. SerializedCache:緩存序列化和反序列化存儲(chǔ)
          6. SoftCache:基于軟引用實(shí)現(xiàn)的緩存管理策略
          7. SynchronizedCache:同步的緩存裝飾器,用于防止多線程并發(fā)訪問
          8. WeakCache:基于弱引用實(shí)現(xiàn)的緩存管理策略

          另外,還有一個(gè)特殊的裝飾器TransactionalCache:事務(wù)性的緩存

          正如大多數(shù)持久層框架一樣,mybatis緩存同樣分為一級(jí)緩存和二級(jí)緩存

          • 一級(jí)緩存,又叫本地緩存,是PerpetualCache類型的永久緩存,保存在執(zhí)行器中(BaseExecutor),而執(zhí)行器又在SqlSession(DefaultSqlSession)中,所以一級(jí)緩存的生命周期與SqlSession是相同的。
          • 二級(jí)緩存,又叫自定義緩存,實(shí)現(xiàn)了Cache接口的類都可以作為二級(jí)緩存,所以可配置如encache等的第三方緩存。二級(jí)緩存以namespace名稱空間為其唯一標(biāo)識(shí),被保存在Configuration核心配置對(duì)象中。

          二級(jí)緩存對(duì)象的默認(rèn)類型為PerpetualCache,如果配置的緩存是默認(rèn)類型,則mybatis會(huì)根據(jù)配置自動(dòng)追加一系列裝飾器。

          Cache對(duì)象之間的引用順序?yàn)椋?/p>

          SynchronizedCache-->LoggingCache-->SerializedCache-->ScheduledCache-->LruCache-->PerpetualCache

          Part10迭代器模式

          迭代器(Iterator)模式,又叫做游標(biāo)(Cursor)模式。GOF給出的定義為:提供一種方法訪問一個(gè)容器(container)對(duì)象中各個(gè)元素,而又不需暴露該對(duì)象的內(nèi)部細(xì)節(jié)。

          Java的Iterator就是迭代器模式的接口,只要實(shí)現(xiàn)了該接口,就相當(dāng)于應(yīng)用了迭代器模式:

          比如Mybatis的PropertyTokenizer是property包中的重量級(jí)類,該類會(huì)被reflection包中其他的類頻繁的引用到。這個(gè)類實(shí)現(xiàn)了Iterator接口,在使用時(shí)經(jīng)常被用到的是Iterator接口中的hasNext這個(gè)函數(shù)。

          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.");
          ?}
          }

          可以看到,這個(gè)類傳入一個(gè)字符串到構(gòu)造函數(shù),然后提供了iterator方法對(duì)解析后的子串進(jìn)行遍歷,是一個(gè)很常用的方法類。

          參考資料:

          • 圖說設(shè)計(jì)模式:http://design-patterns.readthedocs.io/zh_CN/latest/index.html
          • 深入淺出Mybatis系列(十)---SQL執(zhí)行流程分析(源碼篇):http://www.cnblogs.com/dongying/p/4142476.html
          • 設(shè)計(jì)模式讀書筆記-----組合模式 http://www.cnblogs.com/chenssy/p/3299719.html
          • Mybatis3.3.x技術(shù)內(nèi)幕(四):五鼠鬧東京之執(zhí)行器Executor設(shè)計(jì)原本 http://blog.csdn.net/wagcy/article/details/32963235
          • mybatis緩存機(jī)制詳解(一)——Cache https://my.oschina.net/lixin91/blog/620068
          -- END?--

          -??| 更多精彩文章 -



          ← 左右滑動(dòng)與Java學(xué)習(xí)之道互動(dòng)交流 →

          加我微信,交個(gè)朋友
          長(zhǎng)按/掃碼添加↑↑↑

          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美久久一二三 | 2016超碰| 口交露脸| 欧美在线三级片 | 国产毛片一区 |