帶著問(wèn)題看源碼之: ibatis/mybatis 源碼解析 -- Interceptor

Interceptor 接口聲明? , 有三個(gè)方法?

Objectintercept(Invocation invocation)throws Throwable;

Objectplugin(Object target);

void setProperties(Properties properties);


問(wèn)題一: 我們的 Interceptor 是何時(shí)被注冊(cè)到 ibatis 的, 注冊(cè)到哪里去了

首先回答注冊(cè)到哪里去: configuration.InterceptorChain 中 , 結(jié)構(gòu)為 :?List?interceptors =new ArrayList();

xml 聲明 Interceptor 的地方 有兩個(gè):?

1.? ibatis 的 config 配置 <plugins> 中配置? , 這個(gè)配置文件 我們一般叫做 mybatis-config.xml?

2. spring 配置數(shù)據(jù)源的地方配置 sqlSessionFactory(class="org.mybatis.spring.SqlSessionFactoryBean") 時(shí) 以property 的方式給sqlSessionFactoryBean 的 plugins 賦值

① 方式聲明的plugin 添加的 configuration的 InterceptorChain 路徑為: SqlSessionFactoryBean.afterPropertiesSet().buildSqlSessionFactory() ---> XmlConfigBuilder.parse().parseConfiguration(XNode root).pluginElement(root.evalNode("plugins")).configuration.addInterceptor(interceptorInstance)?

進(jìn)而調(diào)用 InterceptorChain的addInterceptor 方法添加 到 InterceptorChain 的?List?interceptors =new ArrayList(); 中

②方式聲明的plugin 添加到 configuration的InterceptorChain 路徑為:

SqlSessionFactoryBean.afterPropertiesSet().buildSqlSessionFactory().configuration.addInterceptor(interceptorInstance)

①和②的實(shí)現(xiàn)邏輯都從?sqlSessionFactoryBean.afterPropertiesSet().buildSqlSessionFactory() 開(kāi)始看就好?

問(wèn)題二:? 我們的Interceptor 是何時(shí)被調(diào)用的? , 初次被調(diào)用時(shí)調(diào)用了哪個(gè)方法?

InterceptorChain 除了問(wèn)題一的 addInterceptor 方法外 還有兩個(gè)方法:

public Object pluginAll(Object target)

public List?getInterceptors()

下面我們看一下 pluginAll 的實(shí)現(xiàn):

public Object pluginAll(Object target) {

for (Interceptor interceptor :interceptors) {

target = interceptor.plugin(target);

? }

return target;

}

初次被調(diào)用時(shí)調(diào)用了哪個(gè)方法: 所以是先調(diào)用了 plugin 方法?。

那是哪里 調(diào)用了 pluginAll 呢 ??

追蹤方法調(diào)用鏈 找到如下 四個(gè)地方:

1.?org.apache.ibatis.session.Configuration#newParameterHandler -->代理ParameterHandler 類(lèi)

2.?org.apache.ibatis.session.Configuration#newResultSetHandler --> 代理ResultSetHandler類(lèi)

3.?org.apache.ibatis.session.Configuration#newStatementHandler --> 代理StatementHandler 類(lèi) (常用)

4.?org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)? --> 代理 Executor 類(lèi)?

處理的目的不同 , 攔截的類(lèi), 方法? 就不同 ;

可以代理的方法 是 上面的類(lèi) 擁有的方法 , 代理哪些方法是 你的 Interceptor 類(lèi)上的@Interceptors 注解的@Signature聲明的

舉個(gè)例子:

@Intercepts({@Signature(

type = Executor.class,

? ? method ="query",

? ? args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}

), @Signature(

type = Executor.class,

? ? method ="query",

? ? args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}

), @Signature(

? ? type = Executor.class,

? ? method ="update",

? ? args = {MappedStatement.class, Object.class}

)})

那么回過(guò)頭來(lái)看, 調(diào)用了我們的Interceptor的 plugin 方法 , 我們應(yīng)該做什么呢??

其實(shí)上面的四個(gè)方法 調(diào)用我們的plugin 時(shí)? , 只是四個(gè)可以攔截的類(lèi) 創(chuàng)建實(shí)例的時(shí)候 , 并沒(méi)有進(jìn)入到? ParameterHandler 、ResultSetHandler、 StatementHandler、Executor 相應(yīng)的可攔截方法,所以我們就需要ibatis 在真正執(zhí)行 @Signature? 指定的那些操作時(shí)告知我們 ,這里常規(guī)的是通過(guò) Plugin.wrap 方法 創(chuàng)建一個(gè) target 的代理類(lèi) , 在代理類(lèi)中判斷target 對(duì)象執(zhí)行的方法是否為我們 @signature 中指定的方法時(shí) 才執(zhí)行我們的邏輯 , 這里為 調(diào)用我們的 Interceptor 方法 , 我們看一下 Plugin的wrap 方法 就知曉了。

調(diào)用我們實(shí)現(xiàn)攔截的目的邏輯 , 在 Plugin 這個(gè)代理類(lèi)的invoke方法中:

這里 是否 調(diào)用 Interceptor 的 Interceptor 方法判斷的依據(jù)是 當(dāng)前 方法是否在 signatureMap 中 , 其實(shí)signatureMap 是在Plugin.wrap 創(chuàng)建Plugin 作為 我們自定義Interceptor 的代理類(lèi)是 處理的 Plugin#getSignatureMap? 主要就是解析當(dāng)前Interceptor 的注解 @Interceptors? 以及 @Signature 將符合條件的方法放到signatureMap 中 ;

我們的Interceptor是何時(shí)被調(diào)用的:

1. 先被調(diào)用了plugin? , 這里調(diào)用 Plugin.wrap 方法 創(chuàng)建了一個(gè) target (ParameterHandler , ResultSetHandler , StatementHandler , Executor) 的代理類(lèi) , 并將當(dāng)前Interceptor 作為參數(shù)傳遞過(guò)去 用于解析 Interceptor 上的@Interceptors , @Signature 注解 識(shí)別相應(yīng)target的 哪些方法需要被代理加入到 signatureMap 中?

2.?當(dāng)target 類(lèi) 執(zhí)行方法調(diào)用時(shí) , 我們的 代理類(lèi)判斷 是否包含在 signatureMap 中, 如果包含則調(diào)用 Interceptor 的 Interceptor 方法

所以: Interceptor 的 Interceptor 方法才是我們 ibatis 攔截器 實(shí)現(xiàn)自定義邏輯的 關(guān)鍵所在 , 攔截不同的方法 我們可以拿到 ibatis 運(yùn)行時(shí)不同的參數(shù)對(duì)象 , 從而 獲取/更改 對(duì)象屬性 以 實(shí)現(xiàn)自己的目的。

實(shí)現(xiàn)自己的目的 還需要對(duì)每一個(gè)參數(shù)的意義和功能有所了解, ibatis 中涉及的專(zhuān)業(yè)名詞 出現(xiàn)的地方 、生效的地方及意義會(huì)在后面帶著問(wèn)題看源碼的文章中講解。

除了 眾所周知的 debug? 看源碼 ,? ?帶著問(wèn)題看源碼可能更容易印象深刻 。? 2019-08-17 22:23?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [上一篇]:Mybatis源碼解析之SqlSession來(lái)自何方 上一篇中我們知道了sqlSession是最后通調(diào)...
    eliter0609閱讀 1,192評(píng)論 0 0
  • 最近項(xiàng)目中需要用到mybatis的擴(kuò)展,就深入看了下mybatis的實(shí)現(xiàn),對(duì)其靈活性和擴(kuò)展性的設(shè)計(jì)思想還是非常佩服...
    Java架構(gòu)_師閱讀 432評(píng)論 0 0
  • Spring+MyBatis實(shí)現(xiàn)讀寫(xiě)分離四種實(shí)現(xiàn)方案整理[http://blog.csdn.net/wuyongd...
    晚歌歌閱讀 10,408評(píng)論 0 6
  • MyBatis提供了一種插件(plugin)的功能,雖然叫做插件,但其實(shí)這是攔截器功能。那么攔截器攔截MyBati...
    七寸知架構(gòu)閱讀 3,318評(píng)論 3 54
  • 豁達(dá)的心胸可以容納百川。人生在世,首先學(xué)會(huì)做個(gè)容納世情萬(wàn)物的入世者,才能擁出世的境界和不一樣的人生。因此,我們應(yīng)該...
    六爸啦啦啦閱讀 521評(píng)論 0 0

友情鏈接更多精彩內(nèi)容