溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor

發(fā)布時間:2020-10-27 13:59:37 來源:億速云 閱讀:791 作者:Leah 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

首先熟悉一下Mybatis的執(zhí)行過程,如下圖:

使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor

類型

先說明Mybatis中可以被攔截的類型具體有以下四種:

1.Executor:攔截執(zhí)行器的方法。
2.ParameterHandler:攔截參數(shù)的處理。
3.ResultHandler:攔截結(jié)果集的處理。
4.StatementHandler:攔截Sql語法構(gòu)建的處理。

規(guī)則

Intercepts注解需要一個Signature(攔截點(diǎn))參數(shù)數(shù)組。通過Signature來指定攔截哪個對象里面的哪個方法。@Intercepts注解定義如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
 /**
  * 定義攔截點(diǎn)
  * 只有符合攔截點(diǎn)的條件才會進(jìn)入到攔截器
  */
 Signature[] value();
}

Signature來指定咱們需要攔截那個類對象的哪個方法。定義如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
 /**
 * 定義攔截的類 Executor、ParameterHandler、StatementHandler、ResultSetHandler當(dāng)中的一個
 */
 Class<&#63;> type();

 /**
 * 在定義攔截類的基礎(chǔ)之上,在定義攔截的方法
 */
 String method();

 /**
 * 在定義攔截方法的基礎(chǔ)之上在定義攔截的方法對應(yīng)的參數(shù),
 * JAVA里面方法可能重載,故注意參數(shù)的類型和順序
 */
 Class<&#63;>[] args();
}

標(biāo)識攔截注解@Intercepts規(guī)則使用,簡單實(shí)例如下:

@Intercepts({//注意看這個大花括號,也就這說這里可以定義多個@Signature對多個地方攔截,都用這個攔截器
  @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)識該類是一個攔截器;
@Signature:指明自定義攔截器需要攔截哪一個類型,哪一個方法;
- type:上述四種類型中的一種;
- method:對應(yīng)接口中的哪類方法(因?yàn)榭赡艽嬖谥剌d方法);
- args:對應(yīng)哪一個方法的入?yún)ⅲ?/p>

method中對應(yīng)四種的類型的方法:

攔截類型攔截方法
Executorupdate, query, flushStatements, commit, rollback,getTransaction, close, isClosed
ParameterHandlergetParameterObject, setParameters
StatementHandlerprepare, parameterize, batch, update, query
ResultSetHandlerhandleResultSets, 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)

這個方法的作用是就是讓mybatis判斷,是否要進(jìn)行攔截,然后做出決定是否生成一個代理。

@Override
 public Object plugin(Object target) {
 //判斷是否攔截這個類型對象(根據(jù)@Intercepts注解決定),然后決定是返回一個代理對象還是返回原對象。
//故我們在實(shí)現(xiàn)plugin方法時,要判斷一下目標(biāo)類型,如果是插件要攔截的對象時才執(zhí)行Plugin.wrap方法,否則的話,直接返回目標(biāo)本身。
  if (target instanceof StatementHandler) {
   return Plugin.wrap(target, this);
  }
  return target;
 }

注意:每經(jīng)過一個攔截器對象都會調(diào)用插件的plugin方法,也就是說,該方法會調(diào)用4次。根據(jù)@Intercepts注解來決定是否進(jìn)行攔截處理。

setProperties(Properties properties)

攔截器需要一些變量對象,而且這個對象是支持可配置的。

實(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)建時間和操作時間相同;update是將版本號+1,統(tǒng)一修改其操作時間。

mybatis-config

<&#63;xml version="1.0" encoding="UTF-8" &#63;>
<!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容器中,否則不會生效。

...//省略其他配置
mybatis:
 config-location: classpath:/mybatis-config.xml

ReflectUtil

...//省略其他配置
mybatis:
 config-location: classpath:/mybatis-config.xml

debug

使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor

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

使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor

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

使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor

關(guān)于使用mybatis 如何自定義實(shí)現(xiàn)攔截器插件Interceptor就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI