Introduction à Mybatis Interceptor
MyBatis offre une fonction de plugin. Bien qu'il s'appelle un plugin, il s'agit en fait d'une fonction intercepteur. Alors, qu'est-ce que l'intercepteur intercepte Mybatis?
Allons sur le site officiel pour jeter un œil:
MyBatis vous permet d'intercepter les appels à un certain point lors de l'exécution d'une instruction mappée. Par défaut, la méthode appelle MyBatis permet aux plugins d'intercepter: inclure:
Nous avons vu certaines méthodes qui peuvent intercepter l'interface de l'exécuteur, telles que la mise à jour, la requête, la validation, le recul et d'autres méthodes, ainsi que certaines méthodes d'autres interfaces
attendez.
Le résumé global est:
Utilisation des intercepteurs
Introduction et configuration de l'intercepteur
Tout d'abord, regardons la définition de l'interface de l'intercepteur MyBatis:
Interface publique Interceptor {Object Intercept (Invocation Invocation) lève le lancement; Plugin d'objet (cible d'objet); void setProperties (propriétés propriétés);}C'est relativement simple, il n'y a que 3 méthodes. MyBatis n'a pas de classe d'implémentation d'interface intercepteur par défaut, et les développeurs peuvent implémenter des intercepteurs qui répondent à leurs besoins.
Voici un exemple d'intercepteur du site officiel de Mybatis:
@Intercepts ({@ signature (type = exécutor.class, method = "update", args = {mappedstatement.class, object.class})}) public class exampleplugin implémente interceptor {public object intercept (invocation invocation) lance throws {return invocation.proed (); } Public Object Plugin (Object Target) {return Plugin.Wrap (Target, This); } public void setProperties (Propriétés Propriétés) {}}Configuration globale XML:
<flugins> <plugin interceptor = "org.format.mybatis.cache.interceptor.exampleplugin"> </ plugin> </ plugins>
Cet intercepteur intercepte la méthode de mise à jour de l'interface de l'exécuteur (en fait, il s'agit des opérations d'addition, de suppression et de modification de SQLSession). Toutes les méthodes de mise à jour qui exécutent exécuteur seront interceptées par l'intercepteur.
Analyse du code source
Analysons le code source derrière ce code.
Tout d'abord, démarrez l'analyse à partir du fichier source-> config:
XMLConfigBuilder Parses Pluginelement Méthode privée de MyBatis Global Configuration Fichier:
Private void Pluginelement (xNode Parent) lève exception {if (parent! = null) {for (xnode child: parent.getchildren ()) {string interceptor = child.getStringAttribute ("interceptor"); Propriétés Propriétés = Child.getChildRenasProperties (); Intercepteur interceptorinstance = (intercepteur) résolveclass (intercepteur) .newinstance (); interceptorinstance.setproperties (propriétés); configuration.addInterceptor (interceptorinstance); }}}Le code d'analyse spécifique est en fait relativement simple, donc je ne le publierai pas. Il instancie principalement la classe représentée par l'attribut intercepteur dans le nœud de plugin par réflexion. Appelez ensuite la méthode AddInterceptor de la configuration de la classe de configuration globale.
public void addInterceptor (interceptor interceptor) {interceptorchain.addinterceptor (intercepteur); }Cet interceptorchain est une propriété interne de configuration, et son type est InterceptorChain, qui est une chaîne intercepteur. Jetons un coup d'œil à sa définition:
classe publique InterceptorChain {private final list <interceptor> interceptors = new ArrayList <interceptor> (); Public Object Pluginall (Object Target) {for (Interceptor Interceptor: Interceptors) {Target = Interceptor.Plugin (Target); } return cible; } public void addInterceptor (interceptor interceptor) {interceptors.add (interceptor); } public List <NERGNETOR> getInterceptors () {return Collection.UnModifiBebleList (Interceptors); }}Maintenant que nous comprenons l'analyse de la configuration de l'intercepteur et la propriété de l'intercepteur, nous examinons maintenant pourquoi l'intercepteur intercepte ces méthodes (méthodes partielles d'exécuteur, de paramètre Handleur, ResultsEthandler):
public ParameterHandler newParameterHandler (maptedstatement mapsstatement, objet ParameterObject, boundsql boundsql) {ParameterHandler ParameterHandler = mappedStatement.getlang (). CreateParameterHandler (MappEdStatement, ParameterObject, Boundsql); ParameterHandler = (ParameterHandler) interceptorchain.pluginall (ParamètreHandler); Return ParameterHandler;} Résultats publicsHandler NewResultSethandler (exécuteur testamentaire, Maptedstatement Maptedstatement, Rowbounds Rowbounds, ParameterHandler ParameterHandler, Resulthandler Resulthandler, BoundsQL Boundsql) {ResultsEthandler ResultsEthandler = New DefaultreSultSetHandler, (exécutoror, MAPPEDSTATEM Resulthandler, BoundsQL, Rowbounds); ResultsEthandler = (ResultsEthandler) interceptorchain.pluginall (ResultsEthandler); Renvoie RésultatsHandler;} public StatementHandler NewStatementHandler (exécuteur testamentaire, mappés de statement MaptedStatement, objet ParameterObject, Rowbounds Rowbounds, Resulthandler Resulthandler, BoundsQL BoundsQL) {StatutHandler Statementhandler = New RoutingStatementHandler (exécuteur, makedstamation boundsql); StatementHandler = (instructionHandler) interceptorchain.pluginall (instructionHandler); return instructionHandler;} public Executor NewExEcutor (transaction transaction, exécutorype exécutorType, boolean autoCommit) {exécutorType = exécutorType == null? DefaultExECutOrType: exécutorType; exécutorType = exécutorType == null? EXECTIONTETTYPE.SIMPLE: EXECTINGETYPE; Exécuteur testamentaire; if (exécutorType.batch == exécutorType) {exécutor = new BatchExecutor (this, transaction); } else if (exécutorType.reuse == exécutorType) {exécutor = new ReuseExECUTOR (this, transaction); } else {exécutor = new SimpleExecutor (this, transaction); } if (cacheenabled) {exécutor = new CachingExecutor (exécutrice, autoCommit); } exécuteur = (exécuteur) interceptorchain.pluginall (exécuteur); return exécuteur exécuteur;}Les 4 méthodes ci-dessus sont toutes des méthodes de configuration. Ces méthodes seront exécutées dans une opération de MyBatis (ajouter, supprimer, modifier et requérir). L'ordre d'exécution est exécuteur, paramètre, résultats, résultats, instruction Handleler (où ParamètreHandler et ResultsEthandler sont créés lors de la création de StatementHandler [3 Classes d'implémentation disponibles callableStationHandler, préparée, constructeurs de ces trois classes d'implémentation, appelez réellement le constructeur de la classe parentale BassestationHandHandler).).
Après que ces 4 méthodes aient instancié l'objet correspondant, ils appellent la méthode du pluginall d'InterceptorChain. Comme mentionné précédemment, le pluginall d'InterceptorChain a été introduit, qui est de traverser tous les intercepteurs, puis d'appeler la méthode du plugin de chaque intercepteur. Remarque: La valeur de retour de la méthode du plugin de l'intercepteur sera attribuée directement à l'objet d'origine.
Étant donné que StatementHandler peut être intercepté, cette interface traite principalement de la construction de la syntaxe SQL. Par conséquent, par exemple, la fonction de la pagination peut être implémentée avec un intercepteur. Il vous suffit de traiter le SQL dans la classe d'implémentation d'interface InstructionHandler dans la méthode du plugin de l'intercepteur, et vous pouvez utiliser la réflexion pour l'implémenter.
MyBatis fournit également des annotations pour @Intercepts et @Signature sur les intercepteurs. L'exemple du site officiel est d'utiliser ces deux annotations, y compris l'utilisation de la classe de plugin:
@OverridePublic Object Plugin (Target de l'objet) {return plugin.wrap (Target, This);}Analysons les codes source de ces 3 "nouvelles combinaisons". Tout d'abord, examinons la méthode de Wrap de la classe du plugin:
Emballage d'objet statique public (cible de l'objet, intercepteur intercepteur) {map <class <?>, set <méthode >> signatMAP = getignaturemap (interceptor); Classe <?> Type = cible.getClass (); Class <?> [] Interfaces = getAllInterfaces (type, signaturmap); if (interfaces.length> 0) {return proxy.newProxyInstance (type.getClassloader (), interfaces, nouveau plugin (cible, interface, signaturemap)); } Return Target;}La classe de plugin implémente l'interface invocationhandler. De toute évidence, nous voyons qu'une classe de proxy dynamique fournie par le JDK lui-même est renvoyée ici. Disséquons d'autres méthodes appelées par cette méthode:
Méthode getsignaturemap:
Map statique privé <classe <?>, set <méthode>> getignaturamap (interceptor interceptor) {intercepts interceptsannotation = interceptor.getClass (). getAnnotation (intercepts.class); if (interceptsannotation == null) {// Issue # 251 Throw New Pluginexception ("Non @Intercepts Annotation a été trouvé dans Interceptor" + interceptor.getClass (). getName ()); } Signature [] sigs = interceptsannotation.value (); Map <class <?>, Set <méthode >> signaturamap = new hashmap <class <?>, Set <méthode >> (); for (Signature Sig: Sigs) {set <méthode> Methods = SignAtremap.get (sig.type ()); if (méthodes == null) {méthodes = new HashSet <méthode> (); SignAtMap.put (sig.type (), méthodes); } try {méthode méthode = sig.type (). getMethod (sig.method (), sig.args ()); méthodes.Add (méthode); } catch (NosuchMethodexception e) {Throw New Pluginexception ("n'a pas pu trouver la méthode sur" + sig.type () + "nommé" + sig.method () + ". Cause:" + e, e); }} return signatMatremap;}La méthode Getsignaturemap Explication: Tout d'abord, vous obtiendrez l'annotation @Interceptors de la classe Interceptor, puis obtiendrez la collection d'annotation @Signature des attributs de cette annotation, puis traversez cette collection, retirez l'attribut de type (type de classe) de ce type d'annotation @Signature, puis obtenez la méthode avec l'attribut de méthode et l'attribut args basé sur ce type. Étant donné que l'attribut @Signature annoté par @Interceptors est une propriété, il renverra éventuellement une carte avec le type comme clé et valeur comme set <méthode>.
@Intercepts ({@ signature (type = exécutor.class, méthode = "update", args = {maptedstatement.class, object.class})})Par exemple, l'annotation @Interceptors renvoie une clé en tant qu'exécuteur et valeur en tant que collection (il n'y a qu'un seul élément dans cette collection, c'est-à-dire l'instance de méthode, cette instance de méthode est la méthode de mise à jour de l'interface exécuteur, et cette méthode a des paramètres de type mappage de type et d'objet). Cette instance de méthode est obtenue en fonction de la méthode et des attributs args de @Signature. Si le paramètre ARGS ne correspond pas à la méthode de type de type de type, une exception sera lancée.
Méthode GetAlLinterfaces:
Classe statique privée <?> [] getAllInterfaces (class <?> Type, map <class <?>, set <méthode >> signatMap) {set <class <? >> interfaces = new hashset <class <? >> (); while (type! = null) {for (class <?> c: type.getInterfaces ()) {if (signatumemap.containsKey (c)) {interfaces.add (c); }} type = type.getsuperclass (); } return interfaces.toArray (nouvelle classe <?> [interfaces.size ()]);}L'explication de la méthode GetAlLinterfaces: Selon la cible d'instance cible (cette cible est la classe que l'intercepteur MyBatis peut intercepter comme mentionné précédemment, exécuteur, paramètre, résultats, ResulteHandler, StatementHandler) et ses classes parentales, renvoie le tableau d'interface contenant l'implémentation cible dans le SignaturMap.
Par conséquent, la fonction de la classe du plugin est d'obtenir le tableau d'attribut @Signature des attributs annotés en fonction de l'annotation @Interceptors, puis d'utiliser la réflexion pour trouver la méthode correspondante en fonction du type, de la méthode et des attributs args de chaque @Signature annoté. Enfin, sur la base de l'interface implémentée par l'objet cible appelé, décidez de renvoyer un objet proxy pour remplacer l'objet cible d'origine.
Par exemple, sur le site officiel de MyBatis, lorsque la configuration appelle la méthode NewExEcutor, la méthode de mise à jour (mapsstatement ms, paramètre objet) de l'interface exécuteur est interceptée par l'intercepteur. Ainsi, la fin est renvoyée avec un plugin de classe proxy, pas un exécuteur testamentaire. Lorsque vous appelez la méthode de cette manière, s'il s'agit d'une classe de proxy, elle sera exécutée:
Public Object Invoke (Proxy d'objet, méthode de la méthode, objet [] args) lève le throwable {try {set <méthode> méthodes = signaturemap.get (method.getDeclarenClass ()); if (méthodes! = null && méthodes.contains (méthode)) {return interceptor.intercept (new invocation (cible, méthode, args)); } return method.invoke (cible, args); } catch (exception e) {lancer exceptionUtil.unwrapthrowable (e); }}C'est vrai, si la méthode correspondante est trouvée et est procassée, la méthode d'interception de l'interface intercepteur sera exécutée.
Cette classe d'invocation est la suivante:
Classe publique Invocation {cible d'objet privé; Méthode privée; objet privé [] args; Invocation publique (objet Target, méthode de la méthode, objet [] args) {this.target = cible; this.method = méthode; this.args = args; } Objet public getTarget () {return Target; } Méthode publique getMethod () {Retour Méthode; } objet public [] getargs () {return args; } public objet procédure () lève invocationTargetException, illégalaccessException {return method.invoke (cible, args); }}Sa méthode de processus consiste à appeler la méthode d'origine (pas de proxy).
Résumer
Parmi les 3 méthodes fournies par l'interface MyBatis Interceptor, la méthode du plugin est utilisée dans le processus de construction de certains processeurs (gestionnaires). La méthode d'interceptor est utilisée pour gérer l'exécution de la classe proxy. La méthode SetProperties est utilisée pour définir les propriétés interceptrices.
En fait, les méthodes fournies par le site officiel de MyBatis pour utiliser @Interceptors et @Signature annotations et classes de plugin pour gérer les intercepteurs ne sont pas nécessairement utilisées directement de cette manière. Nous pouvons également abandonner ces trois classes et effectuer directement des opérations correspondantes en fonction du type de l'instance cible dans la méthode du plugin.
Dans l'ensemble, l'intercepteur MyBatis est encore très simple. L'intercepteur lui-même ne nécessite pas trop de points de connaissance, mais l'apprentissage de l'intercepteur nécessite une familiarité avec chaque interface dans Mybatis, car l'intercepteur implique les points de connaissance de chaque interface.
Résumer
Ce qui précède est une exploration des principes de Mybatis Interceptor introduits par l'éditeur. J'espère que ce sera utile à tout le monde. Si vous avez des questions, veuillez me laisser un message et l'éditeur répondra à tout le monde à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!