Explication détaillée du proxy dynamique JDK
Cet article présente principalement les principes de base du proxy dynamique JDK, afin que tout le monde puisse comprendre plus profondément le proxy JDK, sache ce que c'est. Comprenez le vrai principe de JDK Dynamic Proxy et son processus de génération. Prenons d'abord une démo simple.
Jdk proxy helloworld
package com.yao.proxy; / ** * créé par Robin * / interface publique Helloworld {void Sayshello ();} package com.yao.proxy; import com.yao.heloworld; / ** * Créé par Robin * / public class helloworldIMPl implémente helloworld {public void Sayhello () {System.out.print ("Hello World"); Package com.yao.proxy; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; / ** * cible de l'objet privé; { System.out.println ("Méthode:" + Method.getName () + "est invoqué!"); package com.yao.proxy; import com.yao.heloworld; import java.lang.reflect.constructor; import java.lang.reflect.invocationhandler; import java.lang.reflect.invocationtargexception; Importer Java.lang.reflect.proxy; / ** * Créé par Robin * / Public Class JDKP.Proxy; / ** * Créé par Robin * / Public Class JDKP.Proxy; / ** * Créé par Robin * / Public Class JDKP.Proxy; / ** * Créé par Robin * / Public Classi OID Main (String [] args) lève NosuchMethodexception, illégalaccessException, InvocationTargetException, InstantiationException {// Il y a deux façons d'écrire ici. Classe <?> Proxyclass = proxy.getProxyClass (jdkproxytest.class.getClassloader (), helloworld.class); ) Cons.Newinstance (ih); Helloworld.sayhello (); // Ce qui suit est une façon plus simple d'écrire, essentiellement la même que celle / * Helloworld Helloworld = (Helloworld). * /}}Exécutez le code ci-dessus et un proxy JDK aussi simple sera implémenté.
Processus de génération d'agents
La raison pour laquelle nous appelons JDK Dynamic Proxy chaque jour est que cette classe de proxy est générée dynamiquement par JDK pour nous au moment de l'exécution. Avant d'expliquer le processus de génération de proxy, nous ajoutons d'abord le paramètre -dsun.menc.proxygenerator.saveneneratedFiles = True au paramètre de démarrage JVM. J'ai utilisé Intellij Idea, et après la génération de la classe de proxy, elle a été placée directement dans le répertoire racine du projet , avec le nom du package spécifique comme structure du répertoire.
Le processus de génération d'une classe proxy comprend principalement deux parties:
La méthode getProxyclass Entrée de la classe proxy: vous devez passer dans le chargeur et l'interface de classe
Appelez ensuite la méthode GetProxyclass0, et l'annotation est très claire. On peut clairement voir ici qu'il existe une limite sur le nombre d'interfaces d'interface, qui ne peuvent pas dépasser 65535. Les informations d'initialisation spécifiques de ProxyclassCache sont les suivantes:
proxyClassCache = new LowerCache <> (new KeyFactory (), new proxyClassFactory ());
La logique spécifique pour la création d'une classe proxy est créée via la méthode d'application de ProxyclassFactory.
La logique dans ProxyclassFactory inclut la logique de création du nom du package, appelant ProxyGenerator.
1. La logique de génération de package par défaut est com.sun.proxy.
2. Une fois que le nom du package est prêt, le proxy bytecode est créé en fonction de l'interface entrante spécifique via ProxyGenerator. Dans la classe proxy, toutes les méthodes proxy logiques sont les mêmes pour appeler la méthode invoquée d'invocationhander.
3. Chargez le bytecode dans le JVM via le chargeur de classe passé: DefinClass0 (chargeur, proxyname, proxyclassFile, 0, proxyClassFile.length);.
Private Static Final Class ProxyclassFactory implémente bifunction <Classloader, classe <?> [], classe <? >> {// Préfixe pour tous les noms de classe de proxy Chargeur de chargeur de classe, Classe <?> [] Interfaces) {map <class <?>, Booléen> interfaceSet = new IdentityHashMap <> (interfaces.length); getName (), false, chargeur);} catch (classNotFoundException e) {} (interfaceclass! = intf) {lancez un nouveau lourdeur illégalArgumentException (Intf + "n'est pas visible à partir de la classe"); le visage n'est pas un duplicata. (interfaceset.put (interfaceclass, boolean.true)! = null) {New illégalArgumentException ("interface répétée:" + interfaceclass.getName (); Interface xy afin que la classe * proxy soit définie dans le même package. * Toutes les interfaces proxy non publiques sont dans le même package. kg = ((n == -1)? "": name.substring (0, n + 1)); proxypkg = pkg;} else if (! pkg.equals (proxyPKg)) {lancez new illégalArgumentException ("interfaces non publiques de différents packages"); Age + ".";} / * * Choisissez un nom pour la classe proxy à générer. NextUniqueNumber.getandIncrement (); String proxyname = proxypkg + proxyclassnameprefix + num; lass (proxyname, interfaces, AccessFlags); proxyClassFile.length);} Catch (classformaterror e) {/ * * Un ClassFormaterror signifie que (les arguments de la procuration proxy * Création de classe (comme les limitations de la machine virtuelle *Nous pouvons décompiler en fonction du bytecode de la classe proxy, et nous pouvons obtenir les résultats suivants.
La structure approximative de l'agent comprend 4 parties:
package com.sun.proxy; import com.yao.heloworld; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.lang.reflect.undeclaredablexect; Méthode statique privée M3; méthode statique privée M2; méthode statique privée M0; Public $ proxy0 (invocationhandler var1) lance {super (var1);} public final boolean equals (objet var1) lance {try {return (booléen) super.h.invoke (ce LaredthrowableException (var4);}} public final void sayshello () lance {try {super.h.invoke (this, m3, (object []) null);} catch (runtimeException | error var2) {throw var2; } catch (RuntimeException | Erreur Var2) {Throw Var2; var3) {lancez un nouveau UNDECLARETHTHROWABLEException (var3);}} public final hashcode () lance {try {return ((Integer) super.h.invoke (this, m0, (objet []) null). 3) Class.forname ("java.lang.object"). GetMethod ("equals", new class [] {class.forname ("java.lang.object")}); éthod ("toString", new class [0]); Classe [0]);} catch (nosuchMethodexception var2) {throw noSuchMethoDerror (var2.GetMessage ();} Catch (var3.GetMessage ();FAQ:
1. TOSTRING () HashCode () Egal () Méthode Calling Logic: Si les méthodes de ces trois objets sont appelées, elles passeront par la logique InvocationHandler comme d'autres méthodes et méthodes d'interface. D'autres méthodes sur les objets ne suivront pas la logique de traitement proxy, mais suivront directement la logique de la méthode sur l'objet hérité par proxy.
2. Lorsque l'interface contient des méthodes d'égal et de ToString HashCode, elle suivra la logique du gestionnaire d'invocation tout comme la gestion des méthodes d'interface ordinaire et déclenchera la logique de la méthode en fonction de la réécriture de l'objet cible;
3. L'interface contient des signatures de méthode en double, qui sont soumises à l'ordre dans lequel l'interface est passé.
Merci d'avoir lu, j'espère que cela peut vous aider.