mybatis 自定義實(shí)現(xiàn)攔截器插件Interceptor示例
首先熟悉一下Mybatis的執(zhí)行過程,如下圖:

類型
先說明Mybatis中可以被攔截的類型具體有以下四種:
1.Executor:攔截執(zhí)行器的方法。
2.ParameterHandler:攔截參數(shù)的處理。
3.ResultHandler:攔截結(jié)果集的處理。
4.StatementHandler:攔截Sql語法構(gòu)建的處理。
規(guī)則
Intercepts注解需要一個(gè)Signature(攔截點(diǎn))參數(shù)數(shù)組。通過Signature來指定攔截哪個(gè)對象里面的哪個(gè)方法。@Intercepts注解定義如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
/**
* 定義攔截點(diǎn)
* 只有符合攔截點(diǎn)的條件才會(huì)進(jìn)入到攔截器
*/
Signature[] value();
}
Signature來指定咱們需要攔截那個(gè)類對象的哪個(gè)方法。定義如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
/**
* 定義攔截的類 Executor、ParameterHandler、StatementHandler、ResultSetHandler當(dāng)中的一個(gè)
*/
Class<?> type();
/**
* 在定義攔截類的基礎(chǔ)之上,在定義攔截的方法
*/
String method();
/**
* 在定義攔截方法的基礎(chǔ)之上在定義攔截的方法對應(yīng)的參數(shù),
* JAVA里面方法可能重載,故注意參數(shù)的類型和順序
*/
Class<?>[] args();
}
標(biāo)識攔截注解@Intercepts規(guī)則使用,簡單實(shí)例如下:
@Intercepts({//注意看這個(gè)大花括號,也就這說這里可以定義多個(gè)@Signature對多個(gè)地方攔截,都用這個(gè)攔截器
@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class}),
@Signature(type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
說明:
@Intercepts:標(biāo)識該類是一個(gè)攔截器;
@Signature:指明自定義攔截器需要攔截哪一個(gè)類型,哪一個(gè)方法;
- type:上述四種類型中的一種;
- method:對應(yīng)接口中的哪類方法(因?yàn)榭赡艽嬖谥剌d方法);
- args:對應(yīng)哪一個(gè)方法的入?yún)ⅲ?/p>
method中對應(yīng)四種的類型的方法:
| 攔截類型 | 攔截方法 |
|---|---|
| Executor | update, query, flushStatements, commit, rollback,getTransaction, close, isClosed |
| ParameterHandler | getParameterObject, setParameters |
| StatementHandler | prepare, parameterize, batch, update, query |
| ResultSetHandler | handleResultSets, handleOutputParameters |
介紹
談到自定義攔截器實(shí)踐部分,主要按照以下三步:
實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口,重寫以下方法:
public interface Interceptor {
Object intercept(Invocation var1) throws Throwable;
Object plugin(Object var1);
void setProperties(Properties var1);
}
添加攔截器注解@Intercepts{...}。具體值遵循上述規(guī)則設(shè)置。
配置文件中添加攔截器。
intercept(Invocation invocation)
從上面我們了解到interceptor能夠攔截的四種類型對象,此處入?yún)?code>invocation便是指攔截到的對象。
舉例說明:攔截**StatementHandler#query(Statement st,ResultHandler rh)**方法,那么Invocation就是該對象。
plugin(Object target)
這個(gè)方法的作用是就是讓mybatis判斷,是否要進(jìn)行攔截,然后做出決定是否生成一個(gè)代理。
@Override
public Object plugin(Object target) {
//判斷是否攔截這個(gè)類型對象(根據(jù)@Intercepts注解決定),然后決定是返回一個(gè)代理對象還是返回原對象。
//故我們在實(shí)現(xiàn)plugin方法時(shí),要判斷一下目標(biāo)類型,如果是插件要攔截的對象時(shí)才執(zhí)行Plugin.wrap方法,否則的話,直接返回目標(biāo)本身。
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
注意:每經(jīng)過一個(gè)攔截器對象都會(huì)調(diào)用插件的plugin方法,也就是說,該方法會(huì)調(diào)用4次。根據(jù)@Intercepts注解來決定是否進(jìn)行攔截處理。
setProperties(Properties properties)
攔截器需要一些變量對象,而且這個(gè)對象是支持可配置的。
實(shí)戰(zhàn)
自定義攔截器
@Intercepts(value = {@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
Object obj = boundSql.getParameterObject();
String sql = boundSql.getSql();
if (sql.trim().toUpperCase().startsWith("INSERT")) {
ReflectUtil.setFieldValue(obj, "rev", 0);
ReflectUtil.setFieldValue(obj, "createTime", new Date());
ReflectUtil.setFieldValue(obj, "operateTime", new Date());
ReflectUtil.setFieldValue(boundSql,"parameterObject", obj);
} else if (sql.trim().toUpperCase().startsWith("UPDATE")) {
sql = sql.replaceAll(" set ", " SET ")
.replaceAll(" Set ", " SET ")
.replaceAll(" SET ", " SET rev = rev+1, operate_time = NOW(), ");
ReflectUtil.setFieldValue(boundSql,"sql", sql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
主要看下核心代碼方法intercept():
這段代碼主要目的:攔截insert和update語句,利用反射機(jī)制,設(shè)置insert語句的參數(shù)rev(版本號,利用樂觀鎖),第一次查詢,故創(chuàng)建時(shí)間和操作時(shí)間相同;update是將版本號+1,統(tǒng)一修改其操作時(shí)間。
mybatis-config
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <plugins> <plugin interceptor="com.qxy.mybatis.interceptor.MyInterceptor"/> </plugins> </configuration>
application.yml
特別重要的一點(diǎn),一定將mybatis-config中的對象注入到Sprint容器中,否則不會(huì)生效。
...//省略其他配置 mybatis: config-location: classpath:/mybatis-config.xml
ReflectUtil
...//省略其他配置 mybatis: config-location: classpath:/mybatis-config.xml
debug

上圖中能夠看到BoundSql對象中主要存儲(chǔ)的屬性值,所以我們自定義攔截器時(shí),主要針對BoundSql的屬性值進(jìn)行修改。
程序代碼沒有走到我們反射機(jī)制設(shè)置值的位置,測試createTime=null;

返回之前,看下BoundSql對象的值,創(chuàng)建時(shí)間已被賦值。

源代碼:https://github.com/stream-source
到此這篇關(guān)于mybatis 自定義實(shí)現(xiàn)攔截器插件Interceptor的文章就介紹到這了,更多相關(guān)mybatis 自定義實(shí)現(xiàn)攔截器插件Interceptor內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從內(nèi)存模型中了解Java final的全部細(xì)節(jié)
關(guān)于final關(guān)鍵字,它也是我們一個(gè)經(jīng)常用的關(guān)鍵字,可以修飾在類上、或者修飾在變量、方法上,以此看來定義它的一些不可變性!像我們經(jīng)常使用的String類中,它便是final來修飾的類,并且它的字符數(shù)組也是被final所修飾的。但是一些final的一些細(xì)節(jié)你真的了解過嗎2022-03-03
spring?retry實(shí)現(xiàn)方法請求重試的使用步驟
這篇文章主要介紹了spring?retry實(shí)現(xiàn)方法請求重試及使用步驟,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
Android 應(yīng)用按返回鍵退向后臺(tái)運(yùn)行實(shí)例代碼
這篇文章主要介紹了Android 應(yīng)用按返回鍵退向后臺(tái)運(yùn)行實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04
基于spring+quartz的分布式定時(shí)任務(wù)框架實(shí)現(xiàn)
在Spring中的定時(shí)任務(wù)功能,最好的辦法當(dāng)然是使用Quartz來實(shí)現(xiàn)。這篇文章主要介紹了基于spring+quartz的分布式定時(shí)任務(wù)框架實(shí)現(xiàn),有興趣的可以了解一下。2017-01-01
Java中Lombok @Value注解導(dǎo)致的variable not been initialized問題
本文主要介紹了Java中Lombok @Value注解導(dǎo)致的variable not been initialized問題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Java數(shù)據(jù)結(jié)構(gòu)之有向圖設(shè)計(jì)與實(shí)現(xiàn)詳解
有向圖是具有方向性的圖,由一組頂點(diǎn)和一組有方向的邊組成,每條方向的邊都連著一對有序的頂點(diǎn)。本文為大家介紹的是有向圖的設(shè)計(jì)與實(shí)現(xiàn),需要的可以參考一下2022-11-11
Springboot?格式化LocalDateTime的方法
這篇文章主要介紹了Springboot格式化LocalDateTime的相關(guān)知識,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
Springboot 如何關(guān)閉自動(dòng)配置
這篇文章主要介紹了Springboot 如何關(guān)閉自動(dòng)配置的操作,具有很好的開車價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

