Mybatis雖然小,但是五髒俱全,而且設計精湛。
這個黑盒背後是怎樣一個設計,下面講講我的了解
<code>Configuration</code> 像是Mybatis的總管,Mybatis的所有配置資訊都存放在這裡,此外,它還提供了設定這些配置資訊的方法。Configuration可以從配置檔案裡擷取屬性值,也可以通過程式直接設定。
用一句話概述Configuration,他<code>類似Spring中的容器概念</code>,而且是中央容器級别,存儲的Mybatis運作所需要的大部分東西。
使用mybatis,我們大部分時間都在幹嘛?在XML寫SQL模闆,或者在接口裡寫SQL模闆
或者
這對于Mybatis架構内部意味着什麼?
1、MappedStatement(映射器)
就像使用Spring,我們寫的<code>Controller類對于Spring 架構來說</code>是在定義<code>BeanDefinition</code>一樣。
當我們在XML配置,在接口裡配置SQL模闆,都是在定義Mybatis的域值<code>MappedStatement</code>
一個SQL模闆對應<code>MappedStatement</code>
mybatis 在啟動時,就是把你定義的SQL模闆,解析為統一的<code>MappedStatement</code>對象,放入到容器<code>Configuration</code>中。每個<code>MappedStatement</code>對象有一個ID屬性。這個id同我們平時mysql庫裡的id差不多意思,都是唯一定位一條SQL模闆,這個id 的命名規則:命名空間+方法名
Spring的BeanDefinition,Mybatis的MappedStatement
2、解析過程
同Spring一樣,我們可以在xml定義Bean,也可以java類裡配置。涉及到兩種加載方式。
這裡簡單提一下兩種方法解析的入口:
1.xml方式的解析:
提供了<code>XMLConfigBuilder</code>元件,解析XML檔案,這個過程既是Configuration容器建立的過程,也是<code>MappedStatement</code>解析過程。
2.與Spring使用時:
會注冊一個<code>MapperFactoryBean</code>,在MapperFactoryBean在執行個體化,執行到<code>afterPropertiesSet()</code>時,觸發<code>MappedStatement</code>的解析
在這裡插入圖檔描述
最終會調用Mybatis提供的一<code>MapperAnnotationBuilder</code> 元件,從其名字也可以看出,這個是處理注解形式的<code>MappedStatement</code>
<code>殊途同歸</code>形容這兩種方式很形象,感興趣的可以看看源碼
1.基本介紹
有了SQL模闆,傳入參數,從資料庫擷取資料,這就是SqlSession幹的工作。
SqlSession代表了我們通過Mybatis與資料庫進行的一次會話。使用Mybatis,我們就是使用<code>SqlSession</code>與資料庫互動的。
我們把SQL模闆的id,即<code>MappedStatement</code> 的id 與 參數告訴SqlSession,SqlSession會根據模闆id找到對應<code>MappedStatement</code> ,然後與資料互動,傳回互動結果
2.分類
DefaultSqlSession:最基礎的sqlsession實作,所有的執行最終都會落在這個<code>DefaultSqlSession</code>上,線程不安全
SqlSessionManager : 線程安全的Sqlsession,通過<code>ThreadLocal</code>實作線程安全。
3.Executor
Sqlsession有點像<code>門面模式</code>,SqlSession是一個門面接口,其内部工作是委托<code>Executor</code>完成的。
我們調用<code>SqlSession</code>的方法,都是由<code>Executor</code>完成的。
1.存在的意義
Mapper的意義在于,讓使用者可以像調用方法一樣執行SQL。
差別于,需要顯示傳入SQL模闆的id,執行SQL的方式。
2.工作原理
代理!!!代理!!! 代理!!!Mapper通過代理機制,實作了這個過程。
<code>1、MapperProxyFactory</code>: 為我們的Mapper接口建立代理。
MapperProxyFactory通過JDK動态代理技術,在記憶體中幫我們建立一個代理類出來。(雖然你看不到,但他确實存在)
<code>2、MapperProxy</code>:就是上面建立代理時的增強
針對非預設,非Object方法(也就是我們的業務方法),會封裝成一個<code>MapperMethod</code>, 調用的是<code>MapperMethod.execute</code>
<code>3、MapperMethod</code>
一個業務方法在執行時,會被封裝成<code>MapperMethod</code>, MapperMethod 執行時,又會去調用了<code>Sqlsession</code>
繞了一周,終究回到了最基本的調用方式上。
總結下:
最基本用法=sqlsession.selectOne(statement.id,參數)
Mapper=User代理類getById<code>---》</code>MapperProxy.invoke方法<code>---》</code>MapperMethod.execute()<code>---》</code>sqlsession.selectOne(statement.id,參數)
顯然這一繞,友善了開發人員,但是對于系統來說帶來的是多餘開銷。
Mybatis 還加入了緩存的設計。
分為一級緩存和二級緩存
1.一級緩存
先看長什麼樣子?原來就是HashMap的封裝
在什麼位置?作為<code>BaseExecutor</code>的一個屬性存在。
<code>Executor</code>上面說過,Sqlsession的能力其實是委托<code>Executor</code>完成的.Executor作為Sqlsession的一個屬性存在。
是以:MyBatis一級緩存的生命周期和SqlSession一緻。
2.二級緩存
2.1基本資訊
二級緩存在設計上相對與一級緩存就比較複雜了。
以xml配置為例,二級緩存需要配置開啟,并配置到需要用到的<code>namespace</code>中。
同一個<code>namespace</code>下的所有<code>MappedStatement</code>共用同一個二級緩存。二級緩存的生命周期跟随整個應用的生命周期,同時二級緩存也實作了同<code>namespace</code>下<code>SqlSession</code>資料的共享。
二級緩存配置開啟後,其資料結構預設也是<code>PerpetualCache</code>。這個和一級緩存的一樣。
但是在建構二級緩存時,mybatis使用了一個典型的設計模式<code>裝飾模式</code>,對<code>PerpetualCache</code>進行了一層層的增強,使得二級緩存成為一個被層層裝飾過的<code>PerpetualCache</code>,每裝飾一層,就有不同的能力,這樣一來,二級緩存就比一級緩存豐富多了。
裝飾類有:
LoggingCache:日志功能,裝飾類,用于記錄緩存的命中率,如果開啟了DEBUG模式,則會輸出命中率日志
LruCache:采用了Lru算法的Cache實作,移除最近最少使用的Key/Value
ScheduledCache: 使其具有定時清除能力
BlockingCache: 使其具有阻塞能力
2.2如何工作
二級緩存的工作原理,還是用到<code>裝飾模式</code>,不過這次裝飾的<code>Executor</code>。使用<code>CachingExecutor</code>去裝飾執行SQL的<code>Executor</code>
當執行查詢時,先從二級緩存中查詢,二級緩存沒有時才去走<code>Executor</code>的查詢
其中<code>TransactionalCacheManager</code> 屬性為二級緩存提供了事務能力。
總結下二級緩存
二級緩存是層層裝飾
二級緩存工作原理是裝飾普通執行器
裝飾執行器使用<code>TransactionalCacheManager</code>為二級緩存提供事務能力
一句話總結mybaits插件:代理,代理,代理,還是代理。
Mybatis的插件原理也是動态代理技術。
以分頁插件為例,
建立完Executor後,會執行插件的<code>plugn</code>方法,插件的<code>plugn</code>會調用<code>Plugin.wrap</code>方法,在此方法中我們看到了我們屬性的JDK動态代理技術。建立<code>Executor</code>的代理類,以Plugin為增強。
最終的執行鍊:Executor代理類方法--》Plugin.invoke方法--》插件.intercept方法--》Executor類方法
介于結果映射比較複雜,再開一篇來細節吧
mybatis可以說将裝飾器模式,動态代理用到了極緻。非常值得我們學習。
架構留給應用者的應該是架構運作的基本機關,也就是域值的概念,應用者隻需要定義原料,然後就是黑盒運作。
例如:
Spring的<code>BeanDefinition</code>
Mybatis的<code>MappedStatement</code>
Mybatis是一個非常值得閱讀的架構,相比于Spring的重,将Mybatis作為第一個源碼學習的架構,非常非常的合适。