1. 攔截器注解
1. mybatis自定義攔截器實現步驟:
- 實現
org.apache.ibatis.plugin.Interceptor
接口。 - 添加攔截器注解
org.apache.ibatis.plugin.Intercepts
。 - 配置文件中添加攔截器。
2. 在mybatis中可被攔截的類型有四種(按照攔截順序):
- Executor:攔截執行器的方法。
- ParameterHandler:攔截參數的處理。
- ResultHandler:攔截結果集的處理。
- StatementHandler:攔截Sql語法構建的處理。
1. 不同攔截類型執行順序:

2. 多個插件攔截的順序?

需要注意的是,因為攔截器Aa和攔截器Bb均是攔截的StatementHandler對象,所以攔截器B在此獲取StatementHandler的時候,獲取的是代理對象。

3. 多個插件plugin()和intercept()方法的執行順序
先執行每個插件的plugin方法,若是@Intercepts注解標明需要攔截該對象,那么生成類型對象的代理對象。(即使該插件需要攔截該類型對象,但是依舊會執行下一個插件的plugin方法)。知道執行完畢所有的plugin方法。在執行每個Intercept方法。
3. 攔截器注解的作用:
自定義攔截器必須使用mybatis提供的注解來聲明我們要攔截的類型對象。
Mybatis插件都要有Intercepts [in特賽婆斯]
注解來指定要攔截哪個對象哪個方法。我們知道,Plugin.wrap方法會返回四大接口對象的代理對象,會攔截所有的方法。在代理對象執行對應方法的時候,會調用InvocationHandler處理器的invoke方法。
4. 攔截器注解的規則:
具體規則如下:
@Intercepts({ @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}), @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}), @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}) })
- @Intercepts:標識該類是一個攔截器;
- @Signature:指明自定義攔截器需要攔截哪一個類型,哪一個方法;
2.1 type:對應四種類型中的一種;
2.2 method:對應接口中的哪類方法(因為可能存在重載方法);
2.3 args:對應哪一個方法;
5. 攔截器可攔截的方法:
攔截的類 | 攔截的方法 |
---|---|
Executor | update, query, flushStatements, commit, rollback,getTransaction, close, isClosed |
ParameterHandler | getParameterObject, setParameters |
StatementHandler | prepare, parameterize, batch, update, query |
ResultSetHandler | handleResultSets, handleOutputParameters |
2. 攔截器方法
2.1 官方插件開發方式
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}) public class TestInterceptor implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { Object target = invocation.getTarget(); //被代理對象 Method method = invocation.getMethod(); //代理方法 Object[] args = invocation.getArgs(); //方法參數 // do something ...... 方法攔截前執行代碼塊 Object result = invocation.proceed(); // do something .......方法攔截后執行代碼塊 return result; } public Object plugin(Object target) { return Plugin.wrap(target, this); } }
2.2 攔截器的方法
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties); }
2.2.1 setProperties方法
如果我們的攔截器需要一些變量對象,而且這個對象是支持可配置的。
類似於Spring中的@Value("${}")從application.properties文件中獲取。
使用方法:
<plugin interceptor="com.plugin.mybatis.MyInterceptor"> <property name="username" value="xxx"/> <property name="password" value="xxx"/> </plugin>
方法中獲取參數:properties.getProperty("username");
問題:但是為什么不直接使用@Value("${}") 獲取變量?
解答:因為mybatis框架本身就是一個可以獨立使用的框架,沒有像Spring這種做了很多的依賴注入。
2.2.2 plugin方法
這個方法的作用是就是讓mybatis判斷,是否要進行攔截,然后做出決定是否生成一個代理。
@Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; }
需要注意的是:每經過一個攔截器對象都會調用插件的plugin方法,也就是說,該方法會調用4次。根據@Intercepts注解來決定是否進行攔截處理。
問題1:
Plugin.wrap(target, this)
方法的作用?
解答:判斷是否攔截這個類型對象(根據@Intercepts注解決定),然后決定是返回一個代理對象還是返回原對象。
故我們在實現plugin方法時,要判斷一下目標類型,是本插件要攔截的對象時才執行Plugin.wrap方法,否則的話,直接返回目標本身。
問題2:攔截器代理對象可能經過多層代理,如何獲取到真實的攔截器對象?
/** * <p> * 獲得真正的處理對象,可能多層代理. * </p> */
2.2.3 intercept(Invocation invocation)方法
我們知道,mybatis只能攔截四種類型的對象。而intercept
方法便是處理攔截到的對象。比如我們要攔截StatementHandler#query(Statement st,ResultHandler rh)
方法,那么Invocation
就是這個對象,Invocation
中有三個參數。
- target:StatementHandler;
- method :query;
- args[]:Statement st,ResultHandler rh
org.apache.ibatis.reflection.SystemMetaObject#forObject
:方便的獲取對象中的值。
案例:將參數拼接到sql語句。
因為已經執行了ParameterHandler攔截器,故Statement對象已經是完全拼接好的SQL語句。