[Serie Mybatis] implementación personalizada del interceptor plug-in Interceptor

Primero familiarícese con el proceso de ejecución de Mybatis, como se muestra a continuación:
Inserte la descripción de la imagen aquí

Tipos de

Primero explique que hay cuatro tipos específicos que se pueden interceptar en Mybatis:

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

regla

La anotación de intersecciones requiere una matriz de parámetros de firma (punto de intersección). Utilice Firma para especificar qué método en qué objeto interceptar. La anotación @Intercepts se define de la siguiente manera:

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

Firma para especificar qué método de ese objeto de clase necesitamos interceptar. Se define de la siguiente manera:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
    
    })
public @interface Signature {
    
    
  /**
   * 定义拦截的类 Executor、ParameterHandler、StatementHandler、ResultSetHandler当中的一个
   */
  Class<?> type();

  /**
   * 在定义拦截类的基础之上,在定义拦截的方法
   */
  String method();

  /**
   * 在定义拦截方法的基础之上在定义拦截的方法对应的参数,
   * JAVA里面方法可能重载,故注意参数的类型和顺序
   */
  Class<?>[] args();
}

Identifica el @Interceptsuso de reglas de anotación de bloqueo , un ejemplo simple es el siguiente:


@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})
})

Explicación:
@Intercepts: identifica que esta clase es un interceptor;
@Signature: indica qué tipo y método el interceptor personalizado necesita interceptar ; -type: uno de los
cuatro tipos anteriores ; -method
: correspondiente a la interfaz Qué tipo de método (porque puede haber métodos sobrecargados);
-args: qué método corresponde a los parámetros de entrada;

methodCorrespondiente a los cuatro tipos de métodos:

Tipo de interceptación Método de interceptación
Ejecutor update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
ParameterHandler getParameterObject, setParameters
StatementHandler preparar, parametrizar, procesar por lotes, actualizar, consultar
ResultSetHandler handleResultSets, handleOutputParameters

Introducción

Cuando se trata de la parte práctica de los interceptores personalizados, siga los siguientes tres pasos:

  1. Para implementar la org.apache.ibatis.plugin.Interceptorinterfaz, vuelva a escribir los siguientes métodos:
public interface Interceptor {
    
    
    Object intercept(Invocation var1) throws Throwable;
    Object plugin(Object var1);
    void setProperties(Properties var1);
}
  1. Agrega anotaciones de interceptor @Intercepts{...}. El valor específico se establece de acuerdo con las reglas anteriores.
  2. Agregue un interceptor al archivo de configuración.

interceptar (invocación de invocación)

De lo anterior hemos aprendido que el interceptor puede interceptar los cuatro tipos de objetos, donde la invocationentrada se refiere al objeto interceptado.
Por ejemplo: intercepte el método ** StatementHandler # query (Statement st, ResultHandler rh) **, luego Invocation es el objeto.

complemento (objetivo del objeto)

La función de este método es permitir que mybatis determine si interceptar y luego tomar la decisión de generar un proxy.

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

Nota: El método de complemento del complemento se llamará cada vez que pase un objeto interceptor, es decir, el método se llamará 4 veces. De acuerdo con la anotación @Intercepts para decidir si interceptar el procesamiento.

setProperties (Propiedades propiedades)

El interceptor necesita algunos objetos variables y este objeto es configurable.

Combate real

  • Interceptor personalizado
@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) {
    
    

    }
}

Mire principalmente el método del código central intercept(): el
propósito principal de este código: interceptar declaraciones de inserción y actualización, usar el mecanismo de reflexión, establecer el parámetro rev (número de versión, usando bloqueo optimista) de la declaración de inserción, la primera consulta, por lo que el tiempo de creación y el tiempo de operación son los mismos ; Actualización es el número de versión +1, modificación unificada de su tiempo de funcionamiento.

  • 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>
  • Un
    punto particularmente importante en application.yml es que los objetos en mybatis-config deben inyectarse en el contenedor Sprint, de lo contrario no surtirá efecto.
...//省略其他配置
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);
        }
    }
}
  • depurar
    Inserte la descripción de la imagen aquí

En la figura anterior, puede ver los valores de los atributos principales almacenados en el objeto BoundSql, por lo que cuando personalizamos el interceptor, modificamos principalmente los valores de los atributos de BoundSql.
El código del programa no llega a la posición donde establecemos el valor del mecanismo de reflexión, prueba createTime = null;
Inserte la descripción de la imagen aquí

Antes de regresar, mire el valor del objeto BoundSql, se ha asignado el tiempo de creación.
Inserte la descripción de la imagen aquí

Código fuente: https://github.com/stream-source

Supongo que te gusta

Origin blog.csdn.net/xuan_lu/article/details/109253213
Recomendado
Clasificación