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?