[Mybatisシリーズ]インターセプタープラグインインターセプターのカスタム実装

以下に示すように、最初にMybatisの実行プロセスをよく理解してください。
ここに写真の説明を挿入

の種類

最初に、Mybatisで傍受できる4つの特定のタイプがあることを説明します。

1.Executor:拦截执行器的方法。
2.ParameterHandler:拦截参数的处理。
3.ResultHandler:拦截结果集的处理。
4.StatementHandler:拦截Sql语法构建的处理。

ルール

Interceptsアノテーションには、Signature(インターセプトポイント)パラメーター配列が必要です。署名を使用して、インターセプトするオブジェクトのメソッドを指定します。@Interceptsアノテーションは次のように定義されています。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
    
    
    /**
     * 定义拦截点
     * 只有符合拦截点的条件才会进入到拦截器
     */
    Signature[] value();
}

そのクラスオブジェクトのどのメソッドをインターセプトする必要があるかを指定するための署名。これは次のように定義されます。

@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:
上記の4つのタイプの1つ; -method
:インターフェースに対応しますどのタイプのメソッド(オーバーロードされたメソッドがある可能性があるため);
-args:どのメソッドが入力パラメーターに対応するか。

method4種類の方法に対応します。

傍受タイプ 傍受方法
エグゼキュータ update、query、flushStatements、commit、rollback、getTransaction、close、isClosed
ParameterHandler getParameterObject、setParameters
StatementHandler 準備、パラメータ化、バッチ処理、更新、クエリ
ResultSetHandler handleResultSets、handleOutputParameters

前書き

カスタムインターセプターの練習部分に関しては、次の3つの手順に従ってください。

  1. org.apache.ibatis.plugin.Interceptorインターフェイスを実装するには、次のメソッドを書き直します。
public interface Interceptor {
    
    
    Object intercept(Invocation var1) throws Throwable;
    Object plugin(Object var1);
    void setProperties(Properties var1);
}
  1. インターセプター注釈を追加し@Intercepts{...}ます。具体的な値は、上記のルールに従って設定されます。
  2. 構成ファイルにインターセプターを追加します。

インターセプト(呼び出し呼び出し)

上記から、インターセプターは4種類のオブジェクトをインターセプトできることがわかりました。ここで、invocation入力はインターセプトされたオブジェクトを参照します。
例:** StatementHandler#query(Statement st、ResultHandler rh)**メソッドをインターセプトすると、Invocationがオブジェクトになります。

プラグイン(オブジェクトターゲット)

このメソッドの機能は、mybatisにインターセプトするかどうかを決定させてから、プロキシを生成するかどうかを決定させることです。

    @Override
    public Object plugin(Object target) {
    
    
    //判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。
//故我们在实现plugin方法时,要判断一下目标类型,如果是插件要拦截的对象时才执行Plugin.wrap方法,否则的话,直接返回目标本身。
        if (target instanceof StatementHandler) {
    
    
            return Plugin.wrap(target, this);
        }
        return target;
    }

注:プラグインのプラグインメソッドは、インターセプターオブジェクトが通過するたびに呼び出されます。つまり、メソッドは4回呼び出されます。@Interceptsアノテーションによると、処理をインターセプトするかどうかを決定します。

setProperties(プロパティプロパティ)

インターセプターにはいくつかの可変オブジェクトが必要であり、このオブジェクトは構成可能です。

実際の戦闘

  • カスタムインターセプター
@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()
このコードの主な目的:挿入ステートメントと更新ステートメントをインターセプトし、リフレクションメカニズムを使用し、挿入ステートメントのパラメーターrev(バージョン番号、オプティミスティックロックを使用)を設定します。これにより、作成時間と操作時間が同じになります。 ;更新はバージョン番号+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
public class ReflectUtil {
    
    

    private ReflectUtil() {
    
    }

    /**
     * 利用反射获取指定对象的指定属性
     * @param obj 目标对象
     * @param fieldName 目标属性
     * @return 目标字段
     */
    private static Field getField(Object obj, String fieldName) {
    
    
        Field field = null;
        for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
    
    
            try {
    
    
                field = clazz.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e) {
    
    
                //这里不用做处理,子类没有该字段,可能父类有,都没有就返回null
            }
        }
        return field;
    }

    /**
     * 利用反射设置指定对象的指定属性为指定的值
     * @param obj 目标对象
     * @param fieldName 目标属性
     * @param fieldValue 目标值
     */
    public static void setFieldValue(Object obj, String fieldName, Object fieldValue) throws IllegalAccessException {
    
    
        Field field = getField(obj, fieldName);
        if (field != null) {
    
    
            field.setAccessible(true);
            field.set(obj, fieldValue);
        }
    }
}
  • デバッグ
    ここに写真の説明を挿入

上の図では、BoundSqlオブジェクトに格納されている主な属性値を確認できるため、インターセプターをカスタマイズするときは、主にBoundSqlの属性値を変更します。
プログラムコードは、リフレクションメカニズムの値を設定する位置に到達しません。testcreateTime= null;
ここに写真の説明を挿入

戻る前に、BoundSqlオブジェクトの値を確認してください。作成時間が割り当てられています。
ここに写真の説明を挿入

ソースコード:https//github.com/stream-source

おすすめ

転載: blog.csdn.net/xuan_lu/article/details/109253213