1. 程式人生 > 程式設計 >mybatis 自定義實現攔截器外掛Interceptor示例

mybatis 自定義實現攔截器外掛Interceptor示例

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

在這裡插入圖片描述

型別

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

1.Executor:攔截執行器的方法。
2.ParameterHandler:攔截引數的處理。
3.ResultHandler:攔截結果集的處理。
4.StatementHandler:攔截Sql語法構建的處理。

規則

Intercepts註解需要一個Signature(攔截點)引數陣列。通過Signature來指定攔截哪個物件裡面的哪個方法。@Intercepts註解定義如下:

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

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

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

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

 /**
 * 在定義攔截方法的基礎之上在定義攔截的方法對應的引數,
 * JAVA裡面方法可能過載,故注意引數的型別和順序
 */
 Class<?>[] args();
}

標識攔截註解@Intercepts規則使用,簡單例項如下:

@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:標識該類是一個攔截器;
@Signature:指明自定義攔截器需要攔截哪一個型別,哪一個方法;
- type:上述四種類型中的一種;
- method:對應介面中的哪類方法(因為可能存在過載方法);
- args:對應哪一個方法的入參;

method中對應四種的型別的方法:

攔截型別 攔截方法
Executor update,query,flushStatements,commit,rollback,getTransaction,close,isClosed
ParameterHandler getParameterObject,setParameters
StatementHandler prepare,parameterize,batch,update,query
ResultSetHandler handleResultSets,handleOutputParameters

介紹

談到自定義攔截器實踐部分,主要按照以下三步:

實現org.apache.ibatis.plugin.Interceptor介面,重寫以下方法:

public interface Interceptor {
 Object intercept(Invocation var1) throws Throwable;
 Object plugin(Object var1);
 void setProperties(Properties var1);
}

新增攔截器註解@Intercepts{...}。具體值遵循上述規則設定。

配置檔案中新增攔截器。

intercept(Invocation invocation)

從上面我們瞭解到interceptor能夠攔截的四種類型物件,此處入參invocation便是指攔截到的物件。
舉例說明:攔截**StatementHandler#query(Statement st,ResultHandler rh)**方法,那麼Invocation就是該物件。

plugin(Object target)

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

@Override
 public Object plugin(Object target) {
 //判斷是否攔截這個型別物件(根據@Intercepts註解決定),然後決定是返回一個代理物件還是返回原物件。
//故我們在實現plugin方法時,要判斷一下目標型別,如果是外掛要攔截的物件時才執行Plugin.wrap方法,否則的話,直接返回目標本身。
  if (target instanceof StatementHandler) {
   return Plugin.wrap(target,this);
  }
  return target;
 }

注意:每經過一個攔截器物件都會呼叫外掛的plugin方法,也就是說,該方法會呼叫4次。根據@Intercepts註解來決定是否進行攔截處理。

setProperties(Properties properties)

攔截器需要一些變數物件,而且這個物件是支援可配置的。

實戰

自定義攔截器

@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語句,利用反射機制,設定insert語句的引數rev(版本號,利用樂觀鎖),第一次查詢,故建立時間和操作時間相同;update是將版本號+1,統一修改其操作時間。

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
特別重要的一點,一定將mybatis-config中的物件注入到Sprint容器中,否則不會生效。

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

ReflectUtil

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

debug

在這裡插入圖片描述

上圖中能夠看到BoundSql物件中主要儲存的屬性值,所以我們自定義攔截器時,主要針對BoundSql的屬性值進行修改。
程式程式碼沒有走到我們反射機制設定值的位置,測試createTime=null;

在這裡插入圖片描述

返回之前,看下BoundSql物件的值,建立時間已被賦值。

在這裡插入圖片描述

原始碼:https://github.com/stream-source

到此這篇關於mybatis 自定義實現攔截器外掛Interceptor的文章就介紹到這了,更多相關mybatis 自定義實現攔截器外掛Interceptor內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!