當前位置:首頁 >  站長 >  編程技術 >  正文

簡單了解mybatis攔截器實現(xiàn)原理及實例

 2020-10-30 16:29  來源: 黎青松SEO博客   我來投稿 撤稿糾錯

  域名預訂/競價,好“米”不錯過

這篇文章主要介紹了簡單了解mybatis*實現(xiàn)原理及實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

例行慣例,先看些基本概念:

1 *的作用就是我們可以攔截某些方法的調(diào)用,在目標方法前后加上我們自己邏輯

2 Mybatis*設計的一個初衷是為了供用戶在某些時候可以實現(xiàn)自己的邏輯而不必去動Mybatis固有的邏輯。

自定義*

* mybatis 自定義*

* 三步驟:

* 1 實現(xiàn) {@link Interceptor} 接口

* 2 添加攔截注解 {@link Intercepts}

* 3 配置文件中添加*

* 1 實現(xiàn) {@link Interceptor} 接口

* 具體作用可以看下面代碼每個方法的注釋

* 2 添加攔截注解 {@link Intercepts}

* mybatis *默認可攔截的類型只有四種,即四種接口類型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler

* 對于我們的自定義*必須使用 mybatis 提供的注解來指明我們要攔截的是四類中的哪一個類接口

* 具體規(guī)則如下:

* a:Intercepts *: 標識我的類是一個*

* b:Signature 署名: 則是指明我們的*需要攔截哪一個接口的哪一個方法

* type 對應四類接口中的某一個,比如是 Executor

* method 對應接口中的哪類方法,比如 Executor 的 update 方法

* args 對應接口中的哪一個方法,比如 Executor 中 query 因為重載原因,方法有多個,args 就是指明參數(shù)類型,從而確定是哪一個方法

* 3 配置文件中添加*

* *其實就是一個 plugin,在 mybatis 核心配置文件中我們需要配置我們的 plugin :

* plugin interceptor="liu.york.mybatis.study.plugin.MyInterceptor"

* property name="username" value="LiuYork"/

* property name="password" value="123456"/

* /plugin

* *順序

* 1 不同*順序:

* Executor - ParameterHandler - StatementHandler - ResultSetHandler

* 2 對于同一個類型的*的不同對象攔截順序:

* 在 mybatis 核心配置文件根據(jù)配置的位置,攔截順序是 從上往下

@Intercepts({

@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),

@Signature(method = "query", type = StatementHandler.class, args = {Statement.class, ResultHandler.class})

public class MyInterceptor implements Interceptor {

* 這個方法很好理解

* 作用只有一個:我們不是攔截方法嗎,攔截之后我們要做什么事情呢?

* 這個方法里面就是我們要做的事情

* 解釋這個方法前,我們一定要理解方法參數(shù) {@link Invocation} 是個什么鬼?

* 1 我們知道,mybatis*默認只能攔截四種類型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler

* 2 不管是哪種代理,代理的目標對象就是我們要攔截對象,舉例說明:

* 比如我們要攔截 {@link Executor#update(MappedStatement ms, Object parameter)} 方法,

* 那么 Invocation 就是這個對象,Invocation 里面有三個參數(shù) target method args

* target 就是 Executor

* method 就是 update

* args 就是 MappedStatement ms, Object parameter

* 如果還是不能理解,我再舉一個需求案例:看下面方法代碼里面的需求

* 該方法在運行時調(diào)用

@Override

public Object intercept(Invocation invocation) throws Throwable {

* 需求:我們需要對所有更新操作前打印查詢語句的 sql 日志

* 那我就可以讓我們的自定義* MyInterceptor 攔截 Executor 的 update 方法,在 update 執(zhí)行前打印sql日志

* 比如我們攔截點是 Executor 的 update 方法 : int update(MappedStatement ms, Object parameter)

* 那當我們?nèi)罩敬蛴〕晒χ?,我們是不是還需要調(diào)用這個query方法呢,如何如調(diào)用呢?

* 所以就出現(xiàn)了 Invocation 對象,它這個時候其實就是一個 Executor,而且 method 對應的就是 query 方法,我們

* 想要調(diào)用這個方法,只需要執(zhí)行 invocation.proceed()

/* 因為我攔截的就是Executor,所以我可以強轉(zhuǎn)為 Executor,默認情況下,這個Executor 是個 SimpleExecutor */

Executor executor = (Executor)invocation.getTarget();

* Executor 的 update 方法里面有一個參數(shù) MappedStatement,它是包含了 sql 語句的,所以我獲取這個對象

* 以下是偽代碼,思路:

* 1 通過反射從 Executor 對象中獲取 MappedStatement 對象

* 2 從 MappedStatement 對象中獲取 SqlSource 對象

* 3 然后從 SqlSource 對象中獲取獲取 BoundSql 對象

* 4 最后通過 BoundSql#getSql 方法獲取 sql

MappedStatement mappedStatement = ReflectUtil.getMethodField(executor, MappedStatement.class);

SqlSource sqlSource = ReflectUtil.getField(mappedStatement, SqlSource.class);

BoundSql boundSql = sqlSource.getBoundSql(args);

String sql = boundSql.getSql();

logger.info(sql);

* 現(xiàn)在日志已經(jīng)打印,需要調(diào)用目標對象的方法完成 update 操作

* 我們直接調(diào)用 invocation.proceed() 方法

* 進入源碼其實就是一個常見的反射調(diào)用 method.invoke(target, args)

* target 對應 Executor對象

* method 對應 Executor的update方法

* args 對應 Executor的update方法的參數(shù)

return invocation.proceed();

* 這個方法也很好理解

* 作用就只有一個:那就是Mybatis在創(chuàng)建*代理時候會判斷一次,當前這個類 MyInterceptor 到底需不需要生成一個代理進行攔截,

* 如果需要攔截,就生成一個代理對象,這個代理就是一個 {@link Plugin},它實現(xiàn)了jdk的動態(tài)代理接口 {@link InvocationHandler},

* 如果不需要代理,則直接返回目標對象本身

* Mybatis為什么會判斷一次是否需要代理呢?

* 默認情況下,Mybatis只能攔截四種類型的接口:Executor、StatementHandler、ParameterHandler 和 ResultSetHandler

* 通過 {@link Intercepts} 和 {@link Signature} 兩個注解[!--empirenews.page--]三個核心方法都加了詳細的注釋,而且結(jié)合案例需求說明問題

那么多文字不想行看,沒關系有概括

總結(jié):

1.在mybatis中可被攔截的類型有四種(按照攔截順序):

Executor:攔截執(zhí)行器的方法。

ParameterHandler:攔截參數(shù)的處理。

ResultHandler:攔截結(jié)果集的處理。

StatementHandler:攔截Sql語法構(gòu)建的處理。

2.各個參數(shù)的含義:

@Intercepts:標識該類是一個*;

@Signature:指明自定義*需要攔截哪一個類型,哪一個方法;

2.1 type:對應四種類型中的一種;

2.2 method:對應接口中的哪類方法(因為可能存在重載方法);

2.3 args:對應哪一個方法;

不知道能否幫助你理解,我的表達能力有限~~~

接下來我們看看 Plugin 類

package org.apache.ibatis.plugin;

* Plugin 類其實就是一個代理類,因為它實現(xiàn)了jdk動態(tài)代理接口 InvocationHandler

* 我們核心只需要關注兩個方法

* wrap:

* 如果看懂了代碼案例1的例子,那么這個方法很理解,這個方法就是 mybatis 提供給開發(fā)人員使用的一個工具類方法,

* 目的就是幫助開發(fā)人員省略掉 反射解析注解 Intercepts 和 Signature,有興趣的可以去看看源碼 Plugin#getSignatureMap 方法

* invoke:

* 這個方法就是根據(jù) wrap 方法的解析結(jié)果,判斷當前*是否需要進行攔截,

* 如果需要攔截:將 目標對象+目標方法+目標參數(shù) 封裝成一個 Invocation 對象,給我們自定義的* MyInterceptor 的 intercept 方法

* 這個時候就剛好對應上了上面案例1中對 intercept 方法的解釋了,它就是我們要處理自己邏輯的方法,

* 處理好了之后是否需要調(diào)用目標對象的方法,比如上面說的 打印了sql語句,是否還要查詢數(shù)據(jù)庫呢?答案是肯定的

* 如果不需要攔截:則直接調(diào)用目標對象的方法

* 比如直接調(diào)用 Executor 的 update 方法進行更新數(shù)據(jù)庫

class Plugin implements InvocationHandler {

public static Object wrap(Object target, Interceptor interceptor) {

// 省略

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 省略

}

貼一段網(wǎng)上的通用解釋吧:

Plugin的wrap方法,它根據(jù)當前的Interceptor上面的注解定義哪些接口需要攔截,然后判斷當前目標對象是否有實現(xiàn)對應需要攔截的接口,如果沒有則返回目標對象本身,如果有則返回一個代理對象。而這個代理對象的InvocationHandler正是一個Plugin。所以當目標對象在執(zhí)行接口方法時,如果是通過代理對象執(zhí)行的,則會調(diào)用對應InvocationHandler的invoke方法,也就是Plugin的invoke方法。

所以接著我們來看一下該invoke方法的內(nèi)容。這里invoke方法的邏輯是:如果當前執(zhí)行的方法是定義好的需要攔截的方法,則把目標對象、要執(zhí)行的方法以及方法參數(shù)封裝成一個Invocation對象,再把封裝好的Invocation作為參數(shù)傳遞給當前*的intercept方法。如果不需要攔截,則直接調(diào)用當前的方法。Invocation中定義了定義了一個proceed方法,其邏輯就是調(diào)用當前方法,所以如果在intercept中需要繼續(xù)調(diào)用當前方法的話可以調(diào)用invocation的procced方法。

這就是Mybatis中實現(xiàn)Interceptor攔截的一個思想

文章轉(zhuǎn)自:黎青松SEO博客

來源地址:http://www.alitaohuo.com/fuwuqi/yunwei/1716.html

申請創(chuàng)業(yè)報道,分享創(chuàng)業(yè)好點子。點擊此處,共同探討創(chuàng)業(yè)新機遇!

相關文章

熱門排行

信息推薦