天天看點

架構源碼學習要從Mybatis開始!

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作為第一個源碼學習的架構,非常非常的合适。

繼續閱讀