¿El principio del complemento Mybatis y cómo lograrlo?

¿Qué es un complemento?
Los complementos son una de las funciones más importantes de Mybatis, que pueden mejorar métodos específicos de componentes específicos.
MyBatis le permite interceptar llamadas en un momento determinado durante la ejecución de la declaración mapeada. Por defecto, MyBatis permite el uso de complementos para interceptar llamadas a métodos, incluyendo:
"Executor": actualización, consulta, flushStatements, commit, rollback, getTransaction, close, isClosed
"ParameterHandler": getParameterObject, setParameters
"ResultSetHandler": handleResultSets, handleOutputParameters
"StatementHandler" ":
¿Cómo personalizar complementos para preparar, parametrizar, procesar por lotes, actualizar, consultar ?
La implementación del complemento es realmente muy simple. Solo necesita implementar la interfaz Interceptor proporcionada por Mybatis. El código fuente es el siguiente:
interfaz pública Interceptor { // Método de intercepción Intercepción de objetos (invocación de invocación) arroja Throwable; // Devuelve el objeto proxy del complemento de objetos interceptor (Objeto objetivo); // Establecer algunas propiedades void setProperties (Propiedades propiedades);





}
Por ejemplo,
hay una demanda: el valor del parámetro de selectByUserId necesita ser manipulado cuando se ejecuta Mybatis.
"Análisis": para modificar los parámetros de entrada de SQL, ¿qué método de qué componente se debe utilizar para interceptar la manipulación? Después de estudiar el código fuente, está claro que el método setParameters () en ParameterHandler maneja los parámetros. Por lo tanto, interceptar este método es definitivamente el más apropiado.
El complemento personalizado es el siguiente:
/ **

  • La anotación @Intercepts marca esto como un interceptor, donde se pueden especificar múltiples @Signature
  • @Signature especifica qué método de los cuatro objetos intercepta el interceptor
  •  type:拦截器的四大对象的类型
    
  •  method:拦截器的方法,方法名
    
  •  args:入参的类型,可以是多个,根据方法的参数指定,以此来区分方法的重载
    

* /
@Intercepts (
{ @Signature (type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}) } ) public class ParameterInterceptor implementa Interceptor { @Override public Object intercept (invocación de invocación) lanza Throwable { System .out.println ("Ejecución del interceptor:" + invocation.getTarget ()); // Objeto de destino Object target = invocation.getTarget (); // Obtiene los valores de todos los atributos en el objeto de destino, porque ParameterHandler usa DefaultParameterHandler, Por lo tanto, todas las propiedades internas están encapsuladas en MetaObject metaObject = SystemMetaObject.forObject (target); // Use xxx.xxx.xx para obtener el valor de la propiedad capa por capa, aquí está el valor de id en mappedStatement String value = ( String) metaObject.getValue ("mappedStatement.id"); // Si es el método de consulta especificado














if ("cn.cb.demo.dao.UserMapper.selectByUserId" .equals (value)) { // Establezca el valor del parámetro en admin_1, es decir, establezca id = admin_1, porque solo hay un parámetro, puede establecerlo así, si lo hay Múltiples necesidades necesitan repetir metaObject.setValue ("parameterObject", "admin_1"); } // ejecutar el método de destino return invocation.proceed (); }





@Override
public Object plugin(Object target) {
    //如果没有特殊定制,直接使用Plugin这个工具类返回一个代理对象即可
    return Plugin.wrap(target, this);
}

@Override
public void setProperties(Properties properties) {
}

}
Método de intercepción: el método que eventualmente interceptará, el método más importante.
Método de complemento: devuelve un objeto proxy. Si no hay ningún requisito especial, simplemente use el complemento de la clase de herramienta Mybatis para regresar.
setProperties: establece algunas propiedades, no importantes.
¿Qué anotaciones se utilizan?
Los complementos personalizados deben usar dos anotaciones, a saber, @Intercepts y @Signature.
@Interceptos: anota en la clase de implementación para indicar que esta clase es una clase de implementación de complemento.
@Signature: como atributo de @Intercepts, indica que algunos métodos en algunos componentes de Mybatis deben mejorarse (se pueden especificar varios). Los atributos comúnmente utilizados son los siguientes:
Clase <?> Tipo (): especifique qué componente (Ejecutor, ParameterHandler, ResultSetHandler, StatementHandler)
Método de cadena (): Especifique qué método en el componente mejorado, escriba directamente el nombre del método.
Class <?> [] Args (): Los parámetros en el método deben ser correspondencia uno a uno, y se pueden escribir múltiples; este atributo es muy reutilizable y distingue los métodos sobrecargados.
¿Cómo inyectar Mybatis?
El complemento se ha definido anteriormente, entonces, ¿cómo inyectarlo en Mybatis para que sea efectivo?

"Premisa": Dado que el entorno de este artículo es SpringBoot + Mybatis, hablemos sobre cómo inyectar complementos en Mybatis en SpringBoot.

En la clase Mybatis AutoConfiguration MybatisAutoConfiguration, al inyectar SqlSessionFactory, hay el siguiente código:

¿Qué es este interceptor en la figura anterior y de dónde proviene? En realidad, es el Interceptor [] obtenido del contenedor, como se muestra en el siguiente fragmento de código: 2

De la figura anterior, sabemos que el complemento eventualmente obtendrá el Bean Interceptor [] del contenedor IOC, por lo que solo necesitamos inyectar este Bean en la clase de configuración, como se muestra en el siguiente código:

/ **

  • @Configuration: esta anotación marca la clase como una clase de configuración
    * /
    @Configuration
    public class MybatisConfig {

    / **

    • @Bean: esta anotación se usa para inyectar un Bean en el contenedor
    • Inyectar interceptor [] Bean
    • @return
      * /
      @Bean
      public Interceptor [] interceptors () { // Crea el complemento ParameterInterceptor ParameterInterceptor parameterInterceptor = new ParameterInterceptor (); // Coloca la matriz y devuelve return new Interceptor [] {parameterInterceptor}; } } Personaliza la prueba en este momento El complemento se ha inyectado en Mybatis, ahora prueba para ver si se puede ejecutar correctamente. El código de prueba es el siguiente: @Test void contextLoads () { // La entrada es 1222 UserInfo userInfo = userMapper.selectByUserId ("1222"); System.out.println (userInfo);












    } El
    código de prueba pasó en 1222, porque el complemento cambió los parámetros de entrada, por lo que se debería consultar a la persona admin_1.
    Análisis del principio del
    complemento El principio del complemento es realmente muy simple: generar un objeto proxy (complemento) cuando se crea el componente e interceptarlo cuando se ejecuta el método del componente. Vamos a introducir en detalle cómo funciona el complemento en la parte inferior de Mybatis.
    Los cuatro componentes principales de Mybatis se crean todos en la clase de configuración Configuración de Mybatis. Los métodos específicos son los siguientes:

// 创建 Ejecutor
público Ejecutor newExecutor (Transacción transacción, ExecutorType ejecutorType) { ejecutorType = ejecutorType == null? defaultExecutorType: ejecutorType; ejecutorTipo = ejecutorTipo == nulo? ExecutorType.SIMPLE: ejecutorType; Albacea albacea; if (ExecutorType.BATCH == ejecutorType) { ejecutor = nuevo BatchExecutor (esto, transacción); } else if (ExecutorType.REUSE == ejecutorType) { ejecutor = new ReuseExecutor (esto, transacción); } else { ejecutor = new SimpleExecutor (esto, transacción); } if (cacheEnabled) { ejecutor = nuevo CachingExecutor (ejecutor); } // 调用 pluginAll 方法 , 生成 代理 对象














ejecutor = (Ejecutor) interceptorChain.pluginAll (ejecutor);
volver albacea;
}

// 创建 ParameterHandler
public ParameterHandler newParameterHandler (MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang (). CreateParameterHandler (mappedStatement, parameterObject, boundSql); // 调用 pluginAll 方法 , 生成 代理 对象parameterHandler = (ParameterHandler) interceptorChain.pluginAll (parameterHandler); return parameterHandler; }




// 创建 ResultSetHandler
public ResultSetHandler newResultSetHandler (Ejecutor ejecutor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler, DefaultHandlerStatement; resultSetHandler // 调用 pluginAll 方法 , 生成 代理 对象 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll (resultSetHandler); return resultSetHandler; }




// Crear StatementHandler
pública StatementHandler newStatementHandler (Ejecutor ejecutor, MappedStatement mappedStatement, objeto parameterObject, RowBounds RowBounds, ResultHandler ResultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler (ejecutor, mappedStatement, parameterObject, RowBounds, ResultHandler, boundSql); // Llamada pluginql Método, generar objeto proxy statementHandler = (StatementHandler) interceptorChain.pluginAll (statementHandler); return statementHandler; } A partir del código fuente anterior, podemos saber que pluginAll () se ejecutará en los métodos de creación de los cuatro componentes principales para generar un objeto proxy. Cómo generarlo se explica en detalle a continuación. ¿Cómo generar objetos proxy? El método pluginAll () se ejecutó en el proceso de creación de los cuatro componentes principales. El código fuente de este método es el siguiente: public Object pluginAll (Object target) { // Recorrer el plugin










for (Interceptor interceptor: interceptors) { // Llamar al método plugin () del plugin target = interceptor.plugin (target); } // Devolver el destino de retorno; } El método pluginAll () es muy simple, llame directamente al método plugin () del plugin en un bucle , Pero estamos llamando Plugin.wrap (target, this) esta línea de código, así que tenemos que mirar el código fuente del método wrap (), como sigue: public static Object wrap (Object target, Interceptor interceptor) { // Obtener la anotación @ La definición de Signature Map <Class <?>, Set> signatureMap = getSignatureMap (interceptor); // Clase de destino Class <?> Type = target.getClass (); // Obtenga la interfaz que necesita ser interceptada Class <?> [] Interfaces = getAllInterfaces (type, signatureMap); if (interfaces.length> 0) { // Generar objeto proxy return Proxy.newProxyInstance ( type.getClassLoader (),


















interfaces,
new Plugin (target, interceptor, signatureMap));
}
return target;
}
La lógica del método Plugin.wrap () es muy simple. Determina si el plug-in está interceptando el componente correspondiente. Si es interceptado, genera un objeto proxy (Plugin) Regresar, regresar directamente sin interceptación, el objeto proxy generado en el ejemplo anterior es el siguiente:
¿Cómo ejecutar?
Lo anterior habló sobre cómo generar un objeto proxy (complemento) de acuerdo con el complemento cuando se inicia Mybatis. Ahora echemos un vistazo a cómo se ejecuta este objeto proxy.
Dado que es un proxy dinámico, el método invoke () definitivamente se ejecutará. El código fuente
invoke () en la clase Plugin es el siguiente: @Override
public Object invoke (Object proxy, Method method, Object [] args) throws Throwable { try { // Get @signature marcado método Set methods = signatureMap.get (method.getDeclaringClass ()); // Si este método es interceptado if (methods! = null && methods.contains (method)) { // Ejecuta directamente la intersección del complemento ( )de esta manera






return interceptor.intercept (new Invocation (target, method, args));
}
// No interceptado, ejecuta el método original
return method.invoke (target, args);
} catch (Exception e) { throw ExceptionUtil.unwrapThrowable (e) ; } } La lógica es muy simple. Si este método es interceptado, se ejecutará el método intercept () del complemento. Si no es interceptado, se ejecutará el método original. O tome el complemento personalizado anterior para ver el proceso de ejecución: setParameters () Este método se llama en PreparedStatementHandler, como se muestra en la siguiente figura: El método invoke () se ejecuta y se encuentra que el método setParameters () está interceptado, por lo que la ejecución directa es interceptada ()método. Resumir el principio del complemento en Mybatis es realmente muy simple, dividido en los siguientes pasos: juzgue si el componente es interceptado cuando se inicia el proyecto y, si no, devuelva el objeto original directamente. Si se intercepta, se devuelve el objeto de proxy dinámico (complemento). Al ejecutar el método en el componente, si no es un objeto proxy, ejecute el método original directamente, si es un objeto proxy, ejecute el método invoke () de Plugin. Análisis del principio del complemento de paginación Aquí hay un asistente de página del complemento de paginación de uso frecuente, Maven depende de lo siguiente:
















com.github.pagehelper
pagehelper
5.1.6

Obviamente, el complemento de paginación también está personalizado de acuerdo con el complemento Mybatis. Observe el código fuente del complemento PageInterceptor de la siguiente manera:
@Intercepts (
{ @Signature (type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature (tipo = Executor.class, método = "consulta", args = {MappedStatement.class, Object.class, RowBounds.class , ResultHandler.class, CacheKey.class, BoundSql.class}), } ) la clase pública PageInterceptor implementa Interceptor {} 1 Longhua Avenue http://www.kinghill.cn/LongHuaDaDao1Hao/index.html





Supongo que te gusta

Origin blog.csdn.net/weixin_45032957/article/details/108636262
Recomendado
Clasificación