簡介:
作者:Autorun
原文地址:https://my.oschina.net/u/2488220/blog/1602384
友情提示:歡迎關註公眾號【芋道原始碼】。?關註後,拉你進【原始碼圈】微信群和【Autorun】搞基嗨皮。
友情提示:歡迎關註公眾號【芋道原始碼】。?關註後,拉你進【原始碼圈】微信群和【Autorun】搞基嗨皮。
友情提示:歡迎關註公眾號【芋道原始碼】。?關註後,拉你進【原始碼圈】微信群和【Autorun】搞基嗨皮。
MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和對映原生資訊,將介面和 Java 的 POJOs(Plain Old Java Objects,普通的 Java物件)對映成資料庫中的記錄。
摘自官網。。
為什麼要使用mybatis,相信看這篇文章的童鞋都有所瞭解,俺這裡就多廢話幾句。在orm框架中,有輕量級的dbutils與mybatis,重量級的有hibernate。
為什麼要選擇mybatis呢?原因如下:
1.簡單,這個無需我多廢話。肯定的啊,使用過的童鞋都知道,在dao層定義一個介面,然後定義一個對應的xml(xml中namespace的值對應介面的全限定名就ok。因為mybatis是透過namespaceid去和介面類進行對映,然後使用動態代理建立介面類的實體方法,參見動態代理).
2.結果集對映,我認為這是mybatis做的最牛的一點,也是選擇他最大的一個理由
3.完善的快取機制(mybatis有1級快取和2級快取),1級快取是基於會話的(Session),2級快取是基於應用級別。這裡不做過多闡述,有興趣的可以參考文章 http://www.iteye.com/topic/1112327/
4.強大的外掛機制。它可以在執行過程中動態的執行你自己的業務邏輯,比如防止sql註入,分頁,sql日誌列印,sql執行耗時等都可以在外掛中做。真正實現了業務與功能分離。讓你隨行所欲的在飛dao層中增加任何你想做的事情。
好了。上面說了那麼多好處,本文中會有點穿插,但重點是外掛機制。廢話不多說,我們開始縷一縷mybatis的流程
當然,最古老的方式就不去講了(就是去呼叫mybatis select, selectOne這種方式),我們直接從介面的玩法中入手
這是mybatis的一張架構圖,從中我們可以看得出。應用程式去呼叫對應的db操作時會經歷 配置->開啟會話->在會話中執行相應的操作,同時會話中包含了jdbc的事務(對,沒錯。就是jdbc的事務)。那麼我們可以看得出來,會話是核心。而配置是關鍵。
我們可以開啟配置類看看,中間有很多屬性。我們絕大多數都可以忽略,但是
這個就是mybatis內部維護的攔截器鏈。我們在解析配置,初始化的時候會給xml中寫入一個攔截器的類,他是從
Configuration.addInterceptor(Interceptor interceptor)
這個方法中寫入。這一點對於本文來講很重要,請大家牢記。
接下來說會話。有心急的小夥伴就會提問,外掛和會話有關係嗎?
必然的事情,要不然我也不會羅裡吧嗦的說這麼多關於會話的東西。我們可以看看sqlsession的結構
可以看得出來,他有2個實現類(用過spring的童鞋應該知道,sqlsessionTemplate是spring對sqlsession的簡單封裝,這裡不再贅述), 那麼就剩下了DefaultSqlSession和SqlSessionManager這兩個類。
那麼我們可以做一個大膽的假設,mybatis他預設開啟的是DefaultSqlSession(其實在原始碼中是的)。我們要構建一個sqlsession的時候會使用 SqlSessionFactoryBuilder 這個類,透過傳遞一個抽象的Reader物件(是不是很眼熟?這玩意就是IO流裡的位元組流基類), 透過這個玩意傳一個xml檔案進來
然後去構建一個SqlSessionFactory物件。
可以看得出,它是new的defaultSqlSessionFactory這個類。我們跟進去看看
這裡沒什麼稀奇的,無非就是把我們檔案中的那些屬性轉移給這個Configuration類中,至於這個類裡面具體有什麼我就不帶大家看了。我們繼續看開啟會話的流程
到這裡他的會話構建就出來了,有心的童鞋這裡應該能看到了吧,我上面說的jdbc事務和DefaultSqlSession
ok,我們開啟這個類看看,裡面全是一些模板方法。俺這裡就不一一贅述
我們的重點是從
執行器(我暫且叫他執行器,也比較形象)開始
他這裡就有好多個實現類了。老規矩。我就不帶著大家找了,直接上圖。預設的執行器型別是SimpleExecutor
找到這裡,我們就會發現,這就是它的增刪改查了。老規矩,不贅述
繼續深入,接下來是外掛部分的重點了:
在開始之前我們需要介紹一下,它的攔截器可以作用於
-
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
-
ParameterHandler (getParameterObject, setParameters)
-
ResultSetHandler (handleResultSets, handleOutputParameters)
-
StatementHandler (prepare, parameterize, batch, update, query)
這些範圍。
我在上面說過,interceptorChain 這個東東很重要,這就是我們的攔截器鏈。我們可以看到他會使用pluginAll(Object target)這個方法將所有攔截器加到攔截器鏈中。
而攔截器類是這樣的。
如果我們需要實現一個自己的攔截器則需要實現這個介面中的方法。
其中
Object intercept(Invocation invocation) throws Throwable; // 這個方法是攔截器的業務方法
Object plugin(Object target); // 這個方法是對攔截器的包裝, 如果不包裝的話它是不會被加入到攔截器鏈中
void setProperties(Properties properties);// 這個方法是設定一些額外的屬性
看明白這個之後我們自己手動去編寫一個攔截器類
/**
* @author Autorun
* Created by Autorun on 2018/1/4.
*/@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}) })
public class LogInterceptor implements Interceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
} @Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
} @Override
public void setProperties(Properties properties) {
}
}
@Intercepts({ @Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class}) })
這一行是標註要攔截哪個類上的方法,引數等。然後使用動態代理的invoke(Object obj, Object… args)方法,執行我們的邏輯
如果有多個攔截器則按照順序(因為他內部是使用的ArrayList容器,有序可重覆,大家都懂得)
具體執行的話是使用Plugin類中的Object invoke(Object proxy, Method method, Object[] args) throws Throwable 方法來做的。可以看得出來。它裡面呼叫了我們編寫的 Object intercept(Invocation invocation) throws Throwable;
之後我們需要把我們寫的攔截器類註入到攔截器鏈中。
在mybatis的配置檔案中加入
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
plugin>
plugins>
使我們的攔截器生效。執行程式就會發現我們的業務在對應的範圍生效了。
文章編寫的比較倉促,如果有問題可以留言給我,我會一一回覆大家。謝謝