歡迎光臨
每天分享高質量文章

原始碼級別解讀 mybatis 外掛

簡介:

作者: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>

使我們的攔截器生效。執行程式就會發現我們的業務在對應的範圍生效了。

文章編寫的比較倉促,如果有問題可以留言給我,我會一一回覆大家。謝謝

贊(0)

分享創造快樂