Introducción al Interceptor MyBatis
MyBatis proporciona una función de complemento. Aunque se llama complemento, en realidad es una función de interceptor. Entonces, ¿qué intercepta el interceptor mybatis?
Vayamos al sitio web oficial para echar un vistazo:
MyBatis le permite interceptar llamadas en cierto punto durante la ejecución de una declaración mapeada. Por defecto, el método llama a myBatis permite que los complementos interceptaran:
Hemos visto algunos métodos que pueden interceptar la interfaz del ejecutor, como actualización, consulta, confirmación, reversión y otros métodos, así como algunos métodos de otras interfaces
esperar.
El resumen general es:
Uso de interceptores
Introducción y configuración del interceptor
Primero, veamos la definición de interfaz del interceptor mybatis:
Interfeptor de interfaz pública {Intercept de objeto (invocación de invocación) lanza Throwable; Complemento de objeto (objetivo de objeto); vacío setProperties (propiedades de propiedades);}Es relativamente simple, solo hay 3 métodos. MyBatis no tiene una clase de implementación de interfaz de interceptor de forma predeterminada, y los desarrolladores pueden implementar interceptores que satisfagan sus necesidades.
Aquí hay un ejemplo de un interceptor del sitio web oficial de MyBatis:
@Intercepts ({@firma (type = ejecutor.class, método = "update", args = {mappedstatement.class, object.class})}) clase pública Ejemplo, implementa el interceptor {público intercept (invocación de invocación) lanza {return invocation.prouce (); } Public Object Plugin (Object Target) {return Plugin.wrap (Target, este); } public void setProperties (propiedades de propiedades) {}}Configuración global de XML:
<glugins> <plugin interceptor = "org.format.mybatis.cache.interceptor.exampleplugin"> </glugin> </glugins>
Este interceptor intercepta el método de actualización de la interfaz del Ejecutor (de hecho, es la adición, eliminación y operaciones de modificación de SQLSession). Todos los métodos de actualización que ejecutan el ejecutor serán interceptados por el interceptor.
Análisis del código fuente
Analicemos el código fuente detrás de este código.
Primero, inicie el análisis desde el archivo de configuración de fuente->:
XMLCONFIGBUIREDER PARSES Método privado Método de MyBatis Global Configuración Archivo:
Private void PlugInElement (xnode parent) lanza la excepción {if (parent! = null) {for (xnode child: parent.getChildren ()) {string interceptor = child.getStingAttribute ("interceptor"); Propiedades Propiedades = Child.getChildrenAsproperties (); Interceptor InterceptorInstance = (Interceptor) resolveclass (Interceptor) .newinstance (); InterceptorInstance.SetProperties (Propiedades); Configuración.addinterceptor (InterceptorInstance); }}}El código de análisis específico es realmente relativamente simple, por lo que no lo publicaré. Principalmente instancias de la clase representada por el atributo interceptor en el nodo del complemento por reflexión. Luego llame al método AddInterceptor de la configuración de la clase de configuración global.
AddInterceptor public Void (Interceptor Interceptor) {InterceptoreRchain.addinterceptor (Interceptor); }Esta InterceptorChain es una propiedad interna de la configuración, y su tipo es InterceptorChin, que es una cadena de interceptor. Echemos un vistazo a su definición:
Public Class InterceptorChain {Lista final privada <Sceptor> Interceptors = new ArrayList <Sceptor> (); Public Object PlugInall (Object Target) {for (Interceptor Interceptor: Interceptors) {Target = Interceptor.Plugin (Target); } objetivo de retorno; } public void addInterceptor (interceptor interceptor) {interceptors.add (interceptor); } Lista pública <Ceptor> getInterceptors () {return Collections.unmodifiablelist (interceptores); }}Ahora que entendemos el análisis de la configuración del interceptor y la propiedad del interceptor, ahora miramos hacia atrás a por qué el interceptor intercepta estos métodos (métodos parciales de ejecutor, parámetrohandler, resultados de resultados):
Public ParameterHandler NewParameterHandler (MappedStatement MappedStatement, Object ParameterObject, BoundSQL BoundSql) {ParameterHandler ParameterHandler = MappedStatement.getLang (). CreateParameterHandler (MappedStatement, ParameteterObject, Boundsql); parameterHandler = (ParameterHandler) InterceptorChain.PlugInall (ParameterHandler); return ParameterHandler;} Public ResultsThandler NewResultSethandler (Ejecutor Ejecutor, MappedStatement MapedStatement, RowBounds RowBounds, ParameterHandler ParameterHandler, ResulThandler ResulThandler, Boundsql BoundSQL) {Resultados de los resultados de la Ejecutación = New AfaultresultsetHandler (Ejecutor, MapatéTER, MAPERSHANTLER, PARMAMETERSHANTLER, PARMAMETHERSHANDLER, PAREMETERSHANTLER, ParametRater, ParametRentShandler, ParametRan Resulthandler, BoundSQL, RowBounds); ResultsThandler = (ResultadosThandler) InterceptorChain.Pluginall (ResultsThandler); return resultadosethandler;} public DeclarationHandler NewStatementHandler (ejecutor ejecutor, mappedStatement mappedStatement, object parametreObject, rowbounds rowbounds, resulThandler resulThandler, boundsql boundsql) {DeclaryHandler DeclaryLer = new RoutiningStatementHandler (ejecutor, ejecutor, mappedStatement, parametEROrtOrtOrtectOrtOrtOrtecter, RowBounds, RowBounds, resulThandler, ROWBAndLer, ROWBODSHANDLER, RESULTHAY boundsql); DeclarationHandler = (DeclarationHandler) InterceptorChain.PlugInall (DeclarationHandler); return DeclaryHandler;} public Ejecutor NewExecutor (Transaction Transaction, ExecToType Executype, Boolean Autocommitmit) {ExecToType = Executortype == NULL? DefaultExeCutortype: Executortype; ejecutype = ejecutorType == null? Ejecutype.simple: ejecutorType; Albacea; if (ejecutype.batch == Ejecutype) {Ejecutor = new BatchExecutor (this, transacción); } else if (ejecutype.reuse == ejecutortype) {ejecutor = new ReuseExeCutor (this, transacción); } else {ejecutor = new SimpleExecutor (this, transacción); } if (cacheenabled) {ejecutor = new CachingExecutor (ejecutor, autocommit); } ejecutor = (ejecutor) interceptorchain.pluginall (ejecutor); devolver ejecutor;}Los 4 métodos anteriores son todos los métodos de configuración. Estos métodos se ejecutarán en una operación de MyBatis (agregar, eliminar, modificar y consultar). El orden de ejecución es el ejecutor, el parameterHandler, los resultados, los datadoshandler (donde se crean ParameterHandler y los resultados de los resultados al crear DeclarationHandler [3 clases de implementación disponibles CallableStatementHandler, PrepareStatementHandler, SimpleStementHandler], y el Constructor llama el constructor [los constructores de estas tres clases de implementación en realidad Llame al constructor de la construcción de la construcción de la construcción de la matriz de la construcción de la construcción de la construcción de la construcción de la construcción de la construcción del constructor.
Después de que estos 4 métodos instanciarán el objeto correspondiente, llamarán al método Pluginall de InterceptorChain. Como se mencionó anteriormente, se ha introducido el complemento de InterceptorChain, que es atravesar todos los interceptores y luego llamar al método de complemento de cada interceptor. Nota: El valor de retorno del método de complemento del interceptor se asignará directamente al objeto original.
Dado que DeclaryHandler puede ser interceptado, esta interfaz se ocupa principalmente de la construcción de la sintaxis SQL. Por lo tanto, por ejemplo, la función de la paginación se puede implementar con un interceptor. Solo necesita procesar el SQL en la clase de implementación de la interfaz de DeclarationHandler en el método de complemento del interceptor, y puede usar la reflexión para implementarla.
MyBatis también proporciona anotaciones para @Intercepts y @Signature sobre interceptores. El ejemplo del sitio web oficial es utilizar estas dos anotaciones, incluido el uso de la clase de complemento:
@OverridePublic Object Plugin (Object Target) {return Plugin.wrap (Target, este);}Analicemos los códigos fuente de estas 3 "nuevas combinaciones". Primero, veamos el método de envoltura de la clase de complemento:
Public Static Object Wrap (Object Target, Interceptor Interceptor) {map <class <?>, establecer <método>> significadoMap = getSignatuMap (interceptor); Clase <?> Type = target.getClass (); Clase <?> [] Interfaces = getAllInterfaces (type, SignatureMap); if (interfaces.length> 0) {return proxy.newproxyInstance (type.getClassLoader (), interfaces, nuevo complemento (target, interfaz, significadoMap)); } objetivo de retorno;}La clase de complemento implementa la interfaz InvocationHandler. Obviamente, vemos que una clase de proxy dinámica proporcionada por el JDK en sí se devuelve aquí. Disecemos a otros métodos llamados por este método:
Método de GetSignatureMap:
Mapa estático privado <class <?>, set <setmet>> getSignatureMap (interceptor interceptor) {intercepts interceptSannotation = interceptor.getClass (). getAnnotation (intercepts.class); if (InterceptSannotation == null) {// Problema #251 Lanza nueva PlugineXception ("No se encontró anotación de @intercepts en Interceptor" + Interceptor.getClass (). GetName ()); } Firma [] sigs = InterceptSannotation.Value (); Map <class <?>, establecer <método>> firreasMap = new HashMap <class <?>, establecer <método> (); for (firma sig: sigs) {set <setmet> métodos = significadoMap.get (sig.Type ()); if (métodos == null) {métodos = new Hashset <Shethod> (); significativoMap.put (sig.Type (), métodos); } try {método método = sig.type (). getMethod (sig.method (), sig.args ()); métodss.add (método); } Catch (nosuchmethodexception e) {Throw New Pluginexception ("no pudo encontrar el método en" + sig.type () + "llamado" + sig.method () + ". Causa:" + E, e); }} return SignaturMap;}La explicación del método GetSignatureMap: Primero, obtendrá la anotación @Interceptors de la clase Interceptor, luego obtendrá la colección de atributos de anotación @Signature de esta anotación, luego atraviesa esta colección, elimine el atributo de tipo (tipo de clase) de la anotación @Signature y luego obtenga el método con el atributo del método y args atributos basados en este tipo. Dado que el atributo @Signature anotado por @Interceptors es una propiedad, eventualmente devolverá un mapa con el tipo como clave y valor como establecido <Método>.
@Intercepts ({@firma (type = ejecutor.class, método = "update", args = {mappedStatement.class, object.class})})Por ejemplo, la anotación @Interceptors devolverá una clave como ejecutor y valor como una colección (solo hay un elemento en esta colección, es decir, la instancia del método, esta instancia de método es el método de actualización de la interfaz del ejecutor, y este método tiene parámetros de tipo mappedstatement y objeto). Esta instancia de método se obtiene en función del método y los atributos de Args de @Signature. Si el parámetro Args no corresponde al método del tipo de tipo de tipo, se lanzará una excepción.
Método GetAllInterfaces:
Clase estática privada <?> [] getAllInterfaces (class <?> Tipo, Map <Class <?>, Set <Method>> SignaturMap) {set <class <? >> interfaces = new Hashset <class <?>> (); while (type! = null) {for (class <?> C: type.getInterfaces ()) {if (significativo.containskey (c)) {interfaces.add (c); }} type = type.getSuperClass (); } return interfaces.toarray (nueva clase <?> [interfaces.size ()]);}La explicación del método GetAllInterfaces: de acuerdo con el objetivo de la instancia de destino (este objetivo es la clase de que el interceptor mybatis puede interceptar como se mencionó anteriormente, ejecutor, parameterhandler, resultados methandler, estateghhandler) y sus clases principales, devolver la matriz de interfaz que contiene la implementación de destino en el signApMap.
Por lo tanto, la función de la clase de complemento es obtener la matriz de atributos @Signature de los atributos anotados basados en la anotación @interceptors, y luego usar la reflexión para encontrar el método correspondiente de acuerdo con el tipo, método y los atributos de cada @signature anotados. Finalmente, según la interfaz implementada por el objeto de destino llamado, decida si devuelve un objeto proxy para reemplazar el objeto de destino original.
Por ejemplo, en el sitio web oficial de MyBatis, cuando la configuración llama al método NewExecutor, el Interceptor interceptó el método Update (MappedStatement MS, parámetro de objeto) de la interfaz del Ejecutor. Entonces, el final se devuelve con un complemento de clase proxy, no con un ejecutor. Al llamar al método de esta manera, si es una clase proxy, se ejecutará:
Public Object Invoke (Object Proxy, Method Method, Object [] args) lanza lando {try {set <método> métodos = signatureMap.get (método.getDeclaringClass ()); if (métodos! = null && métodss.contains (método)) {return interceptor.intercept (nueva invocación (target, método, args)); } return Method.Invoke (Target, Args); } catch (Exception e) {Throw ExceptionUtil.unwrapThrowable (e); }}Así es, si el método correspondiente se encuentra y se proxyan, se ejecutará el método de interceptor de la interfaz del interceptor.
Esta clase de invocación es la siguiente:
Invocación de clase pública {objetivo de objeto privado; método privado método; objeto privado [] args; Invocación pública (objetivo de objeto, método método, objeto [] args) {this.target = target; this.method = método; this.args = args; } Public Object getTarget () {return Target; } método público getMethod () {Método de retorno; } objeto público [] getArgs () {return args; } public objeto procediendo () lanza InvocatEdArgetException, ilegalAccessException {return Method.Invoke (Target, Args); }}Su método de proceso es llamar al método original (sin proxy).
Resumir
Entre los 3 métodos proporcionados por la interfaz del Interceptor MyBatis, el método de complemento se utiliza en el proceso de construcción de ciertos procesadores (manejadores). El método de interceptor se utiliza para manejar la ejecución de la clase proxy. El método SetProperties se utiliza para establecer las propiedades del interceptor.
De hecho, los métodos proporcionados por el sitio web oficial de MyBatis para usar @Interceptors y @Signature Annotations y clases de complementos para manejar interceptores no se usan necesariamente directamente de esta manera. También podemos abandonar estas tres clases y realizar directamente las operaciones correspondientes en función del tipo de instancia de destino dentro del método del complemento.
En general, el Interceptor MyBatis sigue siendo muy simple. El interceptor en sí no requiere demasiados puntos de conocimiento, pero aprender el interceptor requiere familiaridad con cada interfaz en MyBatis, porque el interceptor implica los puntos de conocimiento de cada interfaz.
Resumir
Lo anterior es una exploración de los principios del Interceptor MyBatis introducido por el editor. Espero que sea útil para todos. Si tiene alguna pregunta, déjame un mensaje y el editor responderá a todos a tiempo. ¡Muchas gracias por su apoyo al sitio web de Wulin.com!