java.lang.instrument Utilisation d'agent
Le package java.lang.instrument a été introduit dans JDK5. Les programmeurs peuvent modifier dynamiquement le code de classe en modifiant le bytecode de la méthode. Ceci est généralement prétraité avant que la méthode principale de la classe ne soit appelée, et est implémentée par Java pour spécifier la classe proxy de la classe. Avant que le code bytecode de la classe ne soit chargé dans le JVM, la méthode de transformation de ClassFileTransformateur sera appelée pour réaliser la fonction de modification de la méthode de classe d'origine et d'implémentation de AOP. L'avantage de cela est qu'il ne produira pas une nouvelle classe comme la technologie Dynamic Proxy ou CGLIB qui implémente AOP, et il n'est pas nécessaire que la classe d'origine ait une interface.
(1) L'agent est un intercepteur avant votre méthode principale, c'est-à-dire le code qui exécute l'agent avant l'exécution de la méthode principale. Le code de l'agent s'exécute dans le même JVM que votre méthode principale, est chargé par le même système de classe système et est géré par la même politique de sécurité et le même contexte. L'agent de nom est un peu trompeur, et ce n'est pas beaucoup le même que l'agent que nous comprenons généralement. L'agent Java est relativement simple à utiliser. Comment écrire un agent Java? Vous n'avez qu'à implémenter la méthode Premain: Public Static Void Premain (String AgentArgs, Instrumentation Inst) Si la définition ci-dessus de Premain ne peut pas être trouvée dans JDK 6, vous essairez également d'appeler la définition prémain suivante: Public Static Void Premain (String AgentArgs)
(2) La classe d'agent doit être tapée dans un package JAR, puis le méta-infr / mainifest.mf à l'intérieur doit contenir l'attribut de classe pré-classe. Voici un exemple de manifeste.mf:
Version manifeste: 1.0 Classe prémaignée: MyAgent1 créé par: 1.6.0_06
Ensuite, ajoutez Manifest.MF à votre package JAR. Ce qui suit est le manifeste des attributs manifestes pour le fichier JAR d'agent: pré-classe de pré-classe Si un proxy est spécifié lorsque le JVM est démarré, cet attribut spécifie la classe de proxy, c'est-à-dire la classe contenant la méthode Premain. Cette propriété est requise si un proxy est spécifié lorsque le JVM est démarré. Si la propriété n'existe pas, le JVM s'abortera. Remarque: cette propriété est un nom de classe, pas un nom ou un chemin de fichier. Classe d'agent Si l'implémentation prend en charge le mécanisme pour démarrer l'agent à un certain moment après le démarrage de la machine virtuelle, cette propriété spécifie la classe d'agent. C'est-à-dire la classe contenant la méthode AgentMain. Cette propriété est requise et le proxy ne sera pas démarré s'il n'existe pas. Remarque: Ceci est le nom de classe, pas le nom ou le chemin du fichier. Boot-Class-Path définit la liste des chemins pour les recherches de chargeur de classe de démarrage. Les chemins représentent des répertoires ou des bibliothèques (généralement référencées sous forme de bibliothèques JAR ou ZIP sur de nombreuses plates-formes). Après un mécanisme spécifique à la plate-forme pour trouver une classe échoue, le chargeur de classe de démarrage recherche ces chemins. Recherchez les chemins dans l'ordre indiqué. Les chemins de la liste sont séparés par un ou plusieurs espaces. Les chemins utilisent la syntaxe des composants de chemin de l'URI hiérarchique. Si le chemin commence par un caractère slash ("/"), c'est un chemin absolu, sinon c'est un chemin relatif. Le chemin relatif est analysé en fonction du chemin absolu du fichier JAR proxy. Ignorez des chemins avec un format incorrect et des chemins inexistants. Si l'agent est démarré à un certain moment après le démarrage de la machine virtuelle, le chemin qui ne représente pas le fichier JAR est ignoré. Cette propriété est facultative. Boolean can-redefine-classes booléen (vrai ou fausse, sans rapport avec le haut et les minuscules). Si les classes requises pour ce proxy peuvent être redéfinies. Les valeurs autres que vraies sont considérées comme fausses. Cette propriété est facultative et la valeur par défaut est fausse. CAN-RETRANSFORM-CLASSES BOOLEAN (VRAI ou FAUX, INFÉRENANT POUR UNE ET MALIFIQUE). Que les classes requises pour ce proxy puissent être reconverties. Les valeurs autres que vraies sont considérées comme fausses. Cette propriété est facultative et la valeur par défaut est fausse. La valeur booléenne du préfixe de la méthode native peut-être (vraie ou fausse, non pertinente pour la casse supérieure et en bas). Si le préfixe de méthode natif requis par ce proxy peut être défini. Les valeurs autres que vraies sont considérées comme fausses. Cette propriété est facultative et la valeur par défaut est fausse.
(3) Tous ces packages JAR Agent seront automatiquement ajoutés au ClassPath du programme. Il n'est donc pas nécessaire de les ajouter manuellement au chemin de classe. Sauf si vous souhaitez spécifier l'ordre des chemins de classe.
(4) Il n'y a pas de limite sur le nombre de paramètres de -javaagent dans un programme Java, vous pouvez donc ajouter autant d'agents Java. Tous les agents Java seront exécutés dans l'ordre que vous définissez. Par exemple:
java -javaagent: myagent1.jar -javaagent: myAgent2.jar -jar myprogram.jar
Supposons que la fonction principale de MyProgram.jar soit dans MyProgram. MyAgent1.jar, MyAgent2.Jar, les classes qui implémentent Premain dans ces deux packages JAR sont MyAgent1, et l'ordre d'exécution du programme MyAgent2 sera:
MyAgent1.premain -> myAgent2.premain -> myprogram.main
(5) En outre, Premain placé après la fonction principale ne sera pas exécuté, par exemple:
java -javaagent: myagent1.jar -jar myprogram.jar -javaagent: myAgent2.jar
MyAgent2 est placé derrière MyProgram.jar, donc le prémain de MyAgent2 ne sera pas exécuté, donc le résultat de l'exécution sera:
MyAgent1.premain -> myprogram.main
(6) Chaque agent Java peut recevoir un paramètre de type chaîne, c'est-à-dire AgentArgs dans Premain. Cet AgentArgs est défini dans l'option Java. Par exemple:
java -javaagent: myAgent2.jar = thisAsagentargs -jar myprogram.jar
La valeur d'agentArgs reçue par Premain dans MyAgent2 sera "ThisAsagentargs" (à l'exclusion des doubles citations).
(7) Instrumentation dans le paramètre: Ajoutez le ClassFileTransformateur défini par le paramètre pour modifier le fichier de classe. Le transformateur personnalisé implémente ici la méthode de transformation, qui fournit une modification du bytecode de la classe à exécuter réellement, et peut même atteindre le point d'exécuter une autre méthode de classe. Par exemple: écriture de la classe d'agent:
package org.toy; import java.lang.instrument.instrumentation; import java.lang.instrument.classFileTransFormer; classe publique perfmonagent {instrumentation statique privée Inst = null; / ** * Cette méthode est appelée avant que la méthode principale de l'application ne soit appelée, * lorsque cet agent est spécifié pour la machine virtuelle Java. ** / public static void premain (String AgentArgs, instrumentation _inst) {System.out.println ("perfmonAgent.premain () a été appelé."); // Initialisez les variables statiques que nous utilisons pour suivre les informations. inst = _inst; // Configurez le transformateur de fichier de classe. CLASSFILETRANSFORMER Trans = new PerfMonxFormer (); System.out.println ("Ajout d'une instance perfmonXformrer au JVM."); Inst.AddTransformateur (trans); }}Écrivez la classe ClassFileTransFormer:
Package org.toy; Importer java.lang.instrument.classFileTransFormer; import java.lang.instrument.illegalclassformatexception; import java.security.protectiondomain; importer javassist.cannotcompileException; import javassist.ctclass; import javassist.ctbehavior; import javassist.class; javassist.notfoundException; import javassist.expr.expreditor; import javassist.expr.methodcall; public class perfmonxformmer implémente classFileTransFormer {byte public [] transform (classloader chargeur, string className, class <?> classBeingRedRetefined, protectionDomainDomain, byte [] classFileFileBuffer) trowselclasswexecexness, byte [] classFileBuffer) trows ILGLOCTORALIEDEXECPEMENT, BYTE [] classFileBuffer) TransfluedClowerDomain, Byte [] ClassFileBuffer) Tourne, illumine, illustre. {octet [] transformé = null; System.out.println ("Transforming" + className); CLASSPOOL POOL = CLASSPOOL.GETDEFAULT (); CTCLASS CL = NULL; essayez {cl = pool.makeclass (new Java.io.ByteArrayInputStream (classFileBuffer)); if (cl.isinterface () == false) {ctbehavior [] méthodes = cl.getDeclaredBeHaviors (); pour (int i = 0; i <méthodes.length; i ++) {if (méthodes [i] .isempty () == false) {domeThod (méthodes [i]); }} transformé = cl.toByteCode (); }} catch (exception e) {System.err.println ("ne pouvait pas instrument" + classname + ", exception:" + e.getMessage ()); } enfin {if (cl! = null) {cl.detach (); }} retour transformé; } private void domethod (méthode ctbehavior) lève NotFoundException, noudCompileException {// méthode.insertFore ("long stime = System.NanoTime ();"); // Method.Insertafter ("System.out.println (/" Leave "+ méthode.getName () +" et time: / "+ (System.NanoTime () - Stime));"); Method.instrument (New Expreditor () {public void Edit (MethodCall m) lance NedCompileException {M.Replace ("{long stime = System.NanoTime (); $ _ = $ procédure ($$); System.out.println (/" "+ M.getClassname () +". "+ M.getMethodname () + +". ": /" + (System.NanoTime () - stime));} ");}}); }}); }}Les deux classes ci-dessus sont le cœur de l'agent. Lorsque JVM commence, perfmonagent.premain sera appelé avant le chargement de l'application. Ensuite, un ClassFileTransforme personnalisé, à savoir PerfmonXformrer, est instancié dans perfmonagent.premain, puis un fichier de classe de classe personnalisé est instancié dans perfmonXformrer, puis l'instance de perfmonxformère est ajoutée à l'instance d'instrumentation (transmise à partir de JVM). Cela fait que perfmonXformère.transform sera appelé lorsque la classe de l'application sera chargée. Vous pouvez modifier la classe chargée dans cette méthode. C'est vraiment magique. Afin de modifier le bytecode de la classe, j'ai utilisé le javassist de JBoss. Bien que vous n'ayez pas à l'utiliser comme ceci, le javassiste de JBoss est vraiment puissant, vous permettant de changer facilement le bytecode de la classe.
Dans la méthode ci-dessus, j'ai changé le bytecode de la classe et ajouté long stime = system.nanotime (); à l'entrée de la méthode de chaque classe, et ajouté System.out.println ("MethodClassName.MethodName:" + (System.NanoTime () - Stime));
Merci d'avoir lu, j'espère que cela peut vous aider. Merci pour votre soutien à ce site!