Grâce à l'analyse des articles précédents, nous savons que la classe de proxy est générée via l'usine Proxyclassfactory de la classe de procuration. Cette classe d'usine appellera la méthode GenerateProxyClass () de la classe ProxyGenerator pour générer le bytecode de la classe proxy. La classe ProxyGenerator est stockée dans le package Sun.Misc. Nous pouvons trouver cette classe via le code source OpenJDK. Le contenu principal de la méthode statique généréeProxyClass () de cette classe est d'appeler la méthode d'instance généréeClassFile () pour générer des fichiers de classe. Jetons un coup d'œil à ce qui se fait dans la méthode GenerateClassFile ().
octet privé [] GenerateClassFile () {// La première étape consiste à assembler toutes les méthodes en objets proxyméthodes // générer d'abord des méthodes proxy telles que ToString, Hashcode, égal, etc. addProxyMethod (equalsMethod, object.class); addProxyMethod (toStringMethod, object.class); // Transférer chaque méthode de chaque interface et générer un objet proxyméthode pour lui pour (int i = 0; i <interfaces.length; i ++) {méthode [] méthodes = interfaces [i] .getMethods (); pour (int j = 0; j <méthodes.length; j ++) {addProxyMethod (méthodes [j], interfaces [i]); }} // Pour les méthodes de proxy avec la même signature, vérifiez si la valeur de retour de la méthode est compatible pour (list <prandymethod> signatures: proxythods.values ()) {checkReturnTypes (sigMethods); } // Étape 2, assemblez toutes les informations sur le champ et les informations de la méthode du fichier de classe à générer, essayez {// ajouter les méthodes de méthode du constructeur.Add (generateConstructor ()); // Transférer la méthode proxy dans le cache pour (list <prandymethod> signalThods: proxymethods.values ()) {for (proxyméthod pm: SignMethods) {// ajouter des champs statiques de la classe proxy, par exemple: méthode statique privée m1; fields.add (new fieldInfo (pm.MethodFieldName, "Ljava / Lang / Reflect / Method;", Acc_private | ACC_STATIC)); // Ajouter des méthodes proxy des méthodes de classe proxy.Add (pm.GenerateMethod ()); }} // Ajouter des méthodes de méthode d'initialisation de champ statique.Add (GenerateStatiCInitializer ()); } catch (ioException e) {lancer un nouveau interneurror ("exception d'E / S inattendue"); } // La méthode de vérification et la collecte de champs ne peuvent pas être supérieures à 65535 if (methods.size ()> 65535) {lancer une nouvelle illégalargumentException ("la limite de méthode dépassée"); } if (fields.size ()> 65535) {Throw New illégalArgumentException ("Field Limit dépasse"); } if (fields.size ()> 65535) {Throw New illégalArgumentException ("Field Limit dépasse"); } // Étape 3, écrivez dans le fichier de classe finale // Vérifiez qu'il y a le nom entièrement qualifié de la classe proxy dans le pool constant cp.getClass (dottoslash (className)); // Vérifiez qu'il y a le nom entièrement qualifié de la classe Parent Class Proxy dans le pool constant, et le nom de la classe parent est: "Java / Lang / Reflect / Proxy" cp.getClass (superclassName); // Vérifiez que le nom complet qualifié de l'interface de classe proxy pour (int i = 0; i <interfaces.length; i ++) {cp.getClass (dottoslash (interfaces [i] .getName ())); } // Suivant pour commencer à écrire le fichier, définissez le pool constant pour lire uniquement cp.setReadOnly (); ByteArrayOutputStream Bout = new ByteArrayOutputStream (); DataOutputStream Dout = new DataOutputStream (Bout); essayez {// 1. Écrivez sur le numéro magique Dout.WriteInt (0xcafeBabe); // 2. Écrivez dans le numéro de version secondaire Dout.WriteShort (classfile_minor_version); // 3. Écrivez sur le numéro de version principale Dout.WriteShort (classfile_major_version); // 4. Écrivez dans le pool constant CP.Write (DOUT); // 5. Modificateur d'accès à écrire Dout.WriteShort (ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // 6. Écrire l'index de classe Dout.WriteShort (cp.getClass (dottoslash (className))); // 7. Écrivez l'index de la classe parent, les classes proxy générées sont héritées de proxy Dout.WriteShort (cp.getClass (superclassName)); // 8. Écrire la valeur du nombre d'interface Dout.WriteShort (interfaces.length); // 9. Écrire l'interface définie pour (int i = 0; i <interfaces.length; i ++) {Dout.WriteShort (cp.getClass (dottoslash (interfaces [i] .getName ()))); } // 10. Écrire des champs de champs Dout.WriteShort (Fields.Size ()); // 11. Écrivez la collection de champs pour (FieldInfo F: Fields) {F.Write (Dout); } // 12. Écriture de Method Count Value Dout.WriteShort (méthodes.size ()); // 13. Écriture de la collection de méthodes pour (méthodeInfo m: méthodes) {M.Write (Dout); } // 14. Écrivez la valeur du nombre de propriétés, le fichier de classe proxy n'a pas d'attributs, il s'agit donc de 0 Dout.WriteShort (0); } catch (ioException e) {lancer un nouveau interneurror ("exception d'E / S inattendue"); } // Convertir en tableau binaire en rendement de sortie.Vous pouvez voir que la méthode GenerateClassFile () est épissée dynamiquement en fonction de la structure du fichier de classe. Qu'est-ce qu'un fichier de classe? Ici, nous expliquerons d'abord que le fichier Java que nous écrivons généralement se termine par .java. Après l'avoir écrit, compilez-le via le compilateur et générez un fichier .class. Ce fichier .class est un fichier de classe. L'exécution des programmes Java ne dépend que des fichiers de classe et n'a rien à voir avec les fichiers Java. Ce fichier de classe décrit les informations d'une classe. Lorsque nous devons utiliser une classe, la machine virtuelle Java chargera le fichier de classe de cette classe à l'avance et effectuera l'initialisation et la vérification connexe. La machine virtuelle Java peut s'assurer que ces tâches seront effectuées avant d'utiliser cette classe. Nous avons juste besoin de l'utiliser avec la tranquillité d'esprit, sans nous soucier de la façon dont la machine virtuelle Java le charge. Bien sûr, les fichiers de classe ne doivent pas nécessairement être compilés en compilant les fichiers Java. Vous pouvez même écrire des fichiers de classe directement via un éditeur de texte. Ici, le proxy dynamique JDK génère dynamiquement des fichiers de classe via des programmes. Revenons à nouveau au code ci-dessus et voyons que la génération du fichier de classe est principalement divisée en trois étapes:
Étape 1: Collectez toutes les méthodes proxy à générer, enveloppez-les dans des objets proxyméthodes et enregistrez-les dans la collection de cartes.
Étape 2: Collectez toutes les informations sur le champ et les informations de la méthode à générer pour le fichier de classe.
Étape 3: Après avoir terminé les travaux ci-dessus, commencez à assembler le fichier de classe.
Nous savons que la partie fondamentale d'une classe est ses champs et ses méthodes. Concentrons-nous sur la deuxième étape pour voir quels champs et méthodes il génère pour la classe de proxy. Dans la deuxième étape, les quatre choses suivantes ont été faites dans l'ordre.
1. Générez un constructeur de paramètres pour la classe proxy, passez dans une référence à l'instance InvocationHandler et appelez le constructeur de paramètres de la classe parent.
2. Iléter sur la collection de cartes des méthodes proxy, générer le domaine statique de type de méthode correspondant pour chaque méthode proxy et l'ajouter à la collection de champs.
3. Ilérer sur la collection de cartes des méthodes de proxy, générer l'objet Méthodinfo correspondant pour chaque méthode proxy et l'ajouter à la collection de méthodes.
4. Générez une méthode d'initialisation statique pour la classe de proxy. Cette méthode d'initialisation statique attribue principalement la référence de chaque méthode proxy au champ statique correspondant.
Grâce à l'analyse ci-dessus, nous pouvons à peu près savoir que JDK Dynamic Proxy générera finalement une classe de proxy avec la structure suivante pour nous:
La classe publique Proxy0 étend Proxy implémente userdao {// étape 1, générer le constructeur proxy0 (invocationHandler h) {super (h); } // Étape 2, générer la méthode statique privée du domaine statique M1; // Méthode HashCode Méthode statique privée M2; // est égal à la méthode Méthode statique privée M3; // Méthode TOSTRING Méthode statique privée M4; // ... // étape 3, générer la méthode proxy @Override public int hashcode () {try {return (int) h.invoke (this, m1, null); } catch (Throwable E) {lancez un nouveau UnclaredthrowableException (e); }} @Override public boolean equals (objet obj) {try {object [] args = new object [] {obj}; retour (boolean) h.invoke (ce, m2, args); } catch (Throwable E) {lancez un nouveau UnclaredthrowableException (e); }} @Override public String toString () {try {return (string) h.invoke (this, m3, null); } catch (Throwable E) {lancez un nouveau UnclaredthrowableException (e); }} @Override public void Save (utilisateur utilisateur) {try {// Construisez le tableau des paramètres, s'il y a plusieurs paramètres ajoutés plus tard, juste objet [] args = nouvel objet [] {user}; H.invoke (ceci, M4, args); } catch (Throwable E) {lancez un nouveau UnclaredthrowableException (e); }} // Étape 4, générez la méthode d'initialisation statique statique {try {class c1 = class.forname (object.class.getName ()); Classe C2 = class.forname (userdao.class.getName ()); m1 = c1.getMethod ("hashcode", null); m2 = c1.getMethod ("equals", new class [] {object.class}); m3 = c1.getMethod ("toString", null); m4 = c2.getMethod ("Save", new class [] {user.class}); // ...} catch (exception e) {e.printStackTrace (); }}}À ce stade, après une analyse en couches et une exploration approfondie du code source JDK, nous avons restauré l'apparition d'origine de la classe de proxy générée dynamiquement, et certaines des questions précédentes ont également été bien expliquées.
1. La classe proxy hérite de la classe Porxy par défaut. Parce que Java ne prend en charge que l'héritage unique, JDK Dynamic Proxy ne peut implémenter que les interfaces.
2. Les méthodes de proxy appellent la méthode invoke () d'invocationHandler, nous devons donc réécrire la méthode invoke () d'invocationHandler.
3. Lors de l'appel de la méthode invoke (), l'instance proxy elle-même, la méthode cible et les paramètres de la méthode cible seront passés. Expliquez comment les paramètres de la méthode invoke () proviennent.
Utilisez le Proxy0 nouvellement construit comme classe proxy pour tester à nouveau, et vous pouvez voir que le résultat final est le même que la classe proxy générée dynamiquement à l'aide de JDK. Encore une fois, notre analyse est fiable et précise. À ce stade, les articles JDK Dynamic Proxy Series ont été annoncés. Grâce à l'analyse de cette série, l'auteur a résolu les doutes de longue date dans son cœur, et je crois que la compréhension des lecteurs du proxy dynamique JDK est devenu un peu plus loin. Cependant, les connaissances sur papier sont toujours superficielles. Si vous souhaitez mieux maîtriser la technologie de proxy dynamique JDK, les lecteurs peuvent se référer à cette série d'articles pour vérifier le code source JDK par eux-mêmes ou échanger une expérience d'apprentissage avec l'auteur, soulignez l'analyse inappropriée de l'auteur, apprenez ensemble et progressez ensemble.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.