La clé de la mise en œuvre du proxy dynamique dans Java est ces deux choses: proxy et invocationHandler. Commençons par la méthode Invoke dans l'interface invocationhandler et expliquons brièvement comment Java implémente le proxy dynamique.
Premièrement, la forme complète de la méthode invoquée est la suivante:
Public Object Invoke (proxy d'objet, méthode de la méthode, objet [] args) lève le throwable {méthode.invoke (obj, args); return null;}Tout d'abord, supposons que la méthode est la méthode appelée, c'est-à-dire la méthode qui doit être exécutée; Args est le paramètre de la méthode; Proxy, quel est ce paramètre? L'implémentation ci-dessus de la méthode invoke () est une forme relativement standard. Nous voyons qu'aucun paramètre proxy n'est utilisé ici. Consultez la description du proxy dans la documentation JDK, comme suit:
Une invocation de méthode sur une instance proxy via l'une de ses interfaces proxy sera envoyée à la méthode invoquée du gestionnaire d'invocation de l'instance, passant l'instance proxy, un objet java.lang.reflect.method identifiant la méthode qui a été invoquée et un tableau d'objet type contenant les arguments.
À partir de cela, nous pouvons savoir que la supposition ci-dessus est correcte, et nous savons également que le paramètre proxy est une instance de la classe proxy.
Pour plus de commodité de l'explication, voici un exemple simple pour implémenter un proxy dynamique.
// Rôle abstrait (Dynamic Proxy ne peut que l'interface proxy) Sujet de l'interface publique {public void request (); } // Rôle réel: implémenté la méthode de demande () de la classe publique de sujet realSubject implémente le sujet {public void request () {System.out.println ("From Real Subject."); }} // implémentation invocationhandler public class dynamicsubject implémente invocationhandler {private objet obj; // c'est l'avantage du proxy dynamique. L'objet encapsulé est de type d'objet et accepte les objets de tout type public dynamicsubject () {} public dynamicsubject (objet obj) {this.obj = obj; } // Cette méthode n'est pas ce que nous montrons pour appeler l'objet public invoquer (proxy d'objet, méthode de la méthode, objet [] args) lance throwable {System.out.println ("avant d'appeler" + méthode); Method.invoke (obj, args); System.out.println ("après appel" + méthode); retourner null; }} // Client: générer une instance proxy et appeler la méthode request () classe publique Client {public static void main (String [] args) lève le throwsable {// TODO Méthode générée automatiquement Classe <?> CLS = Rs.GetClass (); // Ce qui suit est une génération unique de sujet proxy sujet sujet = (sujet) proxy.newProxyInstance (cls.getClassloader (), cls.getInterfaces (), ds); // Ici, vous pouvez prouver que le sujet est une instance de proxy en exécutant les résultats. Cette instance met en œuvre le sujet d'interface sujet.out.println (instance de sujet proxy); // Ici, vous pouvez voir que la classe de classe du sujet est $ proxy0. Cette classe $ proxy0 hérite du proxy et implémente le System d'interface sujet.out.println ("La classe de la classe du sujet est:" + sujet.getClass (). ToString ()); System.out.print ("Les propriétés du sujet sont:"); Champ [] field = sujet.getClass (). GetDeclaredFields (); pour (champ f: champ) {System.out.print (f.getName () + ","); } Les méthodes de System.out.print ("/ n" + "Sujet sont:"); Method [] Method = sujet.GetClass (). GetDeclaredMethods (); pour (méthode m: méthode) {System.out.print (m.getName () + ","); } La classe parent de System.out.println ("/ n" + "Sujet est:" + sujet.getClass (). GetSuperClass ()); System.out.print ("/ n" + "Sujet implémente l'interface:"); Classe <?> [] Interfaces = sujet.getClass (). GetInterfaces (); pour (class <?> i: interfaces) {System.out.print (i.getName () + ","); } System.out.println ("/ n / n" + "Le résultat de l'exécution est:"); sujet.request (); }} Le résultat de l'opération est le suivant: Le nom du package est omis ici, *** à la place
vrai
La classe de la classe du sujet est: Class $ proxy0
Les propriétés dans le sujet sont: M1, M3, M0, M2,
Les méthodes de sujet sont: Demande, HashCode, égaux, toString,
La classe parent du sujet est: classe java.lang.reflect.proxy
L'interface implémentée par le sujet est: cn.edu.ustc.damicproxy.subject,
Le résultat de l'opération est:
Avant d'appeler public abstrait void ***. sujet.request ()
Du sujet réel.
Après avoir appelé le public abstrait vide ***. Sujet.request ()
PS: Les informations sur ce résultat sont très importantes, du moins pour moi. Parce que la cause profonde de mes étourdissements dans le proxy dynamique est que j'ai mal compris le sujet ci-dessus. J'étais une fois confus sur la façon dont la dernière demande d'appel () a été connectée à invoke (), et comment Invoke savait que la demande existe. En fait, True et Class $ proxy0 ci-dessus peuvent résoudre de nombreuses questions, et couplée au code source de $ proxy0 qui sera mentionné ci-dessous, il peut résoudre complètement les doutes du proxy dynamique.
À partir du code et des résultats ci-dessus, nous pouvons voir que nous n'avons pas appelé la méthode invoke () comme indiqué, mais cette méthode s'est exécutée. Analysons l'ensemble du processus ci-dessous:
À en juger par le code du client, vous pouvez utiliser la méthode NewProxyInstance comme percée. Examinons d'abord le code source de la méthode NewProxyInstance dans la classe Proxy:
Objet statique public NewProxyInstance (Classloader Loader, class <?> [] Interfaces, invocationHandler h) lève illégalArgumentException {if (h == null) {throw new NullPointerException (); } / * * Recherchez ou générez la classe de proxy conçue. * / Class cl = getProxyClass (chargeur, interfaces); / * * Invoquez son constructeur avec le gestionnaire d'invocation conçu. * / try {/ * * Le code source proxy a la définition suivante: * Classe statique finale privée [] ConstructorAms = {invocationHandler.class}; * Cons est la méthode du constructeur avec les paramètres formels du type d'invocationHandler * / constructeur Cons = cl.getConstructor (ConstructorArams); return (object) Cons.NewInstance (nouvel objet [] {h}); } catch (NosuchMethodexception e) {Throw New Internerror (e.ToString ()); } catch (illégalaccessException e) {lancez un nouveau Internerror (e.toString ()); } Catch (InstantiationException e) {Throw New Internerror (e.toString ()); } catch (invocationTargetException e) {Throw new Internerror (e.toString ()); }} Proxy.newproxyinstance (classloader chargeur, classe <?> [] Interfaces, invocationhandler h) fait les choses suivantes.
(1) Appelez la méthode getProxyClass (chargeur, interfaces) en fonction du chargeur et des interfaces de paramètres, créez la classe proxy $ proxy0. $ Proxy0 class, implémente les interfaces interfaces et inhérit la classe proxy.
(2) Instanciate $ proxy0 et pass dynamicsubject dans le constructeur, puis $ proxy0 appelle le constructeur du proxy de classe parent et attribue une valeur à h, comme suit:
classe proxy {invocationHandler h = null; Proxy protégé (invocationHandler h) {this.h = h; } ...}Jetons un coup d'œil au code source qui hérite de $ proxy0 de Proxy:
La classe finale publique $ proxy0 étend le proxy implémente le sujet {méthode statique privée m1; Méthode statique privée M0; Méthode statique privée M3; Méthode statique privée M2; statique {try {m1 = class.forname ("java.lang.object"). getMethod ("equals", new class [] {class.forname ("java.lang.object")}); m0 = class.forname ("java.lang.object"). getMethod ("hashcode", new class [0]); m3 = class.forname ("***. realSubject"). getMethod ("demande", new class [0]); m2 = class.forname ("java.lang.object"). getMethod ("toString", new class [0]); } catch (NosuchMethodexception NosuchMethodexception) {Throw New NosuchMethoDerror (NosuchMethodexception.GetMessage ()); } catch (classNotFoundException classNotFoundException) {lancer un nouveau noclassdeffoundError (classNotFoundException.getMessage ()); }} // public static $ proxy0 (invocationHandler invocationHandler) {super (invocationHandler); } @Override public final booléen equals (objet obj) {try {return ((boolean) super.h.invoke (this, m1, nouvel objet [] {obj})) .booleAlValue (); } Catch (Thrownable Throwable) {Throw New UnclaredThrowableException (Thrownable); }} @Override public final int hashcode () {try {return ((Integer) super.h.invoke (this, m0, null)). IntValue (); } Catch (Thrownable Throwable) {Throw New UnclaredThrowableException (Thrownable); }} public final void request () {try {super.h.invoke (this, m3, null); retour; } catch (error e) {} catch (throwable throwable) {lance new UnclaredThrowableException (Throwable); }} @Override public final String toString () {try {return (string) super.h.invoke (this, m2, null); } Catch (Thrownable Throwable) {Throw New UnclaredThrowableException (Thrownable); }}}Ensuite, jetez l'instance $ proxy0 résultante dans un sujet et attribuez la référence au sujet. Lorsque la méthode Subject.Request () est exécutée, la méthode Request () dans la classe $ proxy0 est appelée, et la méthode invoke () de H dans le proxy de la classe parent est appelée. C'est-à-dire invocationhandler.invoke ().
PS: 1. Une chose à noter est que la méthode GetProxyclass dans la classe de proxy renvoie la classe de classe de proxy. La raison en est que j'ai fait une erreur de bas niveau au début, pensant que le retour est "classe de classe de la classe de procuration" -! Il est recommandé de jeter un œil au code source de GetProxyclass, qui est très long =. =
2. À partir du code source de $ proxy0, on peut voir que la classe de proxy dynamique indique non seulement les méthodes dans l'interface définie d'affichage, mais aussi les proxies héréditaires des trois méthodes d'égal (), HashCode () et ToString () dans l'objet racine de Java, et seulement ces trois méthodes.
Q: Jusqu'à présent, il y a encore une question. Le premier paramètre de la méthode Invoke est une instance de proxy (pour être précis, l'instance de $ proxy0 est enfin utilisée), mais à quoi sert? Ou, comment la fonction apparaît-elle dans le programme?
R: De mon niveau actuel, ce paramètre proxy n'a aucun effet. Dans l'ensemble du mécanisme de proxy dynamique, le paramètre proxy de la méthode invoquée dans InvocationHandler n'est pas utilisé. Le paramètre passé est en fait une instance de la classe proxy. Je pense que cela pourrait être de laisser les programmeurs utiliser la réflexion dans la méthode Invoke pour obtenir des informations sur la classe proxy.
Résumer
Ce qui précède est l'intégralité du contenu de cet article sur la méthode invoke () dans InvocationHandler. J'espère que ce sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à ce site:
Explication détaillée du proxy statique du printemps et du code de proxy dynamique
Exemple de méthode d'injection de dépendance au cadre de printemps
Java Programming Implémentation de SpringMVC Simple Login Exemple
S'il y a des lacunes, veuillez laisser un message pour le signaler.