java.lang.instrument agente Uso
El paquete java.lang.instrument se introdujo en JDK5. Los programadores pueden modificar dinámicamente el código de clase modificando el bytecode del método. Esto generalmente se preprocesan antes del método principal de la clase, y Java implementa especificar la clase proxy de la clase. Antes de que se cargue el código de bytet de la clase en el JVM, se llamará al método de transformación de ClassFiletransformer para realizar la función de modificar el método de clase original e implementar AOP. La ventaja de esto es que no producirá una nueva clase como proxy dinámico o tecnología CGLIB que implementa AOP, y no hay necesidad de que la clase original tenga una interfaz.
(1) El agente es un interceptor antes de su método principal, es decir, el código que ejecuta el agente antes de ejecutar el método principal. El código del agente se ejecuta en el mismo JVM que su método principal, está cargado por el mismo cargador de clases del sistema y es administrado por la misma política y contexto de seguridad. El agente del nombre es un poco engañoso, y no es lo mismo que el agente que generalmente entendemos. El agente de Java es relativamente simple de usar. ¿Cómo escribir un agente de Java? Solo necesita implementar el método Premain: Public static void Premain (String AgenteRargs, Instrumentation Inst) Si la definición anterior de Premain no se puede encontrar en JDK 6, también intentará llamar a la siguiente definición de Premain: Public static void Premain (String Agargs)
(2) La clase de agente debe escribirse en un paquete JAR, y luego el metainf/mainifest.mf adentro debe contener el atributo de clase Premain. Aquí hay un ejemplo de manifiesto.mf:
Versión del manifiesto: 1.0 Premain-Class: MyAgent1 creado por: 1.6.0_06
Luego agregue Manifest.mf a su paquete jar. El siguiente es el manifiesto manifiesto manifiesto para el archivo JAR de agente: Premain-Class Si se especifica un proxy cuando se inicia el JVM, este atributo especifica la clase proxy, es decir, la clase que contiene el método Premain. Se requiere esta propiedad si se especifica un proxy cuando se inicia el JVM. Si la propiedad no existe, el JVM abortará. Nota: Esta propiedad es un nombre de clase, no un nombre o ruta de archivo. Clase de agente Si la implementación admite el mecanismo para iniciar el agente en un momento determinado después de que se inicia la VM, esta propiedad especifica la clase de agente. Es decir, la clase que contiene el método AgentMain. Esta propiedad es necesaria y el proxy no se iniciará si no existe. Nota: Este es el nombre de clase, no el nombre o ruta del archivo. Boot-Class-Path Establece la lista de rutas para las búsquedas de cargador de clase de arranque. Las rutas representan directorios o bibliotecas (generalmente referenciadas como bibliotecas jar o zip en muchas plataformas). Después de que falla un mecanismo específico para la plataforma para encontrar una clase, el cargador de clase de arranque busca estas rutas. Busque las rutas en el pedido enumerado. Las rutas en la lista están separadas por uno o más espacios. Rutas Use la sintaxis del componente de ruta del URI jerárquico. Si la ruta comienza con un carácter de corte ("/"), es un camino absoluto, de lo contrario es una ruta relativa. La ruta relativa se analiza en función de la ruta absoluta del archivo jar proxy. Ignore las rutas con formato incorrecto y rutas inexistentes. Si el agente se inicia en un momento determinado después de que se inicia la VM, se ignora la ruta que no representa el archivo JAR. Esta propiedad es opcional. Las clases de redefinición booleana (verdadero o falso, irrelevante para la caja superior y minúscula). Si las clases requeridas para este proxy pueden redefinirse. Los valores que no sean verdaderos se consideran falsos. Esta propiedad es opcional y el valor predeterminado es falso. Las clases de retransformación de la lata booleana (verdadero o falso, irrelevante para la caja superior y minúscula). Si se pueden reconvertir las clases requeridas para este proxy. Los valores que no sean verdaderos se consideran falsos. Esta propiedad es opcional y el valor predeterminado es falso. Valor booleano de prefijo de método-set-set-native (verdadero o falso, irrelevante para la caja superior y minúscula). Se puede establecer si el prefijo del método nativo requerido por este proxy. Los valores que no sean verdaderos se consideran falsos. Esta propiedad es opcional y el valor predeterminado es falso.
(3) Todos estos paquetes JAR de agentes se agregarán automáticamente al ClassPath del programa. Por lo tanto, no hay necesidad de agregarlos a la classpath manualmente. A menos que desee especificar el orden de classpaths.
(4) No hay límite en el número de parámetros de -javaagent en un programa Java, por lo que puede agregar tantos agentes de Java. Todos los agentes de Java serán ejecutados en el orden que defina. Por ejemplo:
java -javaagent: myagent1.jar -javaagent: myagent2.jar -jar myprogram.jar
Supongamos que la función principal en myprogram.jar está en myprogram. Myagent1.jar, myagent2.Jar, las clases que implementan premain en estos dos paquetes jar son myagent1, y la orden de ejecución del programa myagent2 será:
Myagent1.preemain -> myagent2.premain -> myprogram.main
(5) Además, Premain colocado después de la función principal no se ejecutará, por ejemplo:
java -javaagent: myagent1.jar -jar myprogram.jar -javaagent: myagent2.jar
Myagent2 se coloca detrás de myprogram.jar, por lo que la premain de myagent2 no se ejecutará, por lo que el resultado de la ejecución será:
Myagent1.premain -> myprogram.main
(6) Cada agente de Java puede recibir un parámetro de tipo de cadena, es decir, agenteargs en premain. Este agenteRargs se define en la opción Java. Por ejemplo:
java -javaagent: myagent2.jar = thisSageNargs -jar myprogram.jar
El valor de los agentes recibidos por Premain en MyAgent2 será "thisSageNARGS" (excluyendo citas dobles).
(7) Instrumentación en el parámetro: Agregue el ClassFiletransformer definido por el parámetro para cambiar el archivo de clase. El transformador personalizado aquí implementa el método de transformación, que proporciona modificación al código de byting de la clase que realmente se ejecutará, e incluso puede alcanzar el punto de ejecutar otro método de clase. Por ejemplo: Clase de agente de escritura:
paquete org.toy; import java.lang.instrument.instrumentation; import java.lang.instrument.classfiletransformer; public class perfmonagent {private static instrumentation inst = null; /** * Este método se llama antes del método principal de la aplicación, * cuando este agente se especifica a la VM Java. **/ public static void premain (String agenteRargs, instrumentación _inst) {system.out.println ("perfmonagent.premain () se llamó"); // Inicializa las variables estáticas que usamos para rastrear la información. inst = _inst; // Configure el transformador de archivo de clase. ClassFiletransformer trans = new PerfMonxformer (); System.out.println ("Agregar una instancia de perfonxformer al jvm"); inst.addtransformer (trans); }}Escribir clase ClassFiletransformer:
paquete org.toy; import java.lang.instrument.classfiletransformer; import java.lang.instrument.illegalClassFormatexception; import java.security.protectionDomain; import javassist.cannotcompileexception; import javassist.classpool; import javassist.ctbehavioVior; javassist.notfoundException; import javassist.expr.expreditor; import javassist.expr.methodcall; public class perfmonxformer implementa classFilErfilErformer {public byte [] transform (classloader Loader, String className, class <?> ClassbeingEnfined, ProtectionDomain ProtectionMain, byte [] classfileBuffer) byte [] transformado = nulo; System.out.println ("transformando" + classname); Classpool Pool = classpool.getDefault (); Ctclass cl = null; Pruebe {cl = prow.makeclass (nuevo java.io.bytearrayinputstream (classfileBuffer)); if (cl.isinterface () == false) {ctbehavior [] métodos = cl.getDeClaredBeHaviors (); for (int i = 0; i <métodss.length; i ++) {if (métodos [i] .isempty () == false) {domineThod (métodos [i]); }} transformado = cl.tobytecode (); }} Catch (Exception e) {System.err.println ("no pudo instrumentar" + classname + ", excepción:" + e.getMessage ()); } finalmente {if (cl! = null) {cl.detach (); }} return transformado; } private void domethod (método ctbehavior) lanza NotFoundException, no se puede hacer CompileException {// Method.insertbefore ("Long STEME = System.nanotime ();"); // Method.instafter ("System.out.println (/" Leave "+Method.getName ()+" y Time:/"+(System.Nanotime ()-STEME));"); Method.instrument (new ExricDitor () {public void Edit (MethodCall M) lanza no puedo CompileException {M.Replace ("{Long Stime = System.Nanotime (); $ _ = $ proced ($$); System.out.println (/" " +M.getClassName () +" +M.getMethodName () + + + + + + + ":/"+(System.nanotime ()-STEME));} ");}}); }}); }}Las dos clases anteriores son el núcleo del agente. Cuando se inicia JVM, se llamará a Perfmonagent.Preemain antes de cargar la aplicación. Luego, un classfiletransforme personalizado, a saber, perfonxformer, se instanciona en perfmonagent.preemain, y luego un classFiletransformer personalizado se instancia en perfmonxformer, y luego la instancia de perfonxformer se agrega a la instancia de instrumentación (transmitida a partir de JVM). Esto hace que PerfMonxFormer.transform se llamará cuando se cargue la clase en la aplicación. Puede cambiar la clase cargada en este método. Es realmente mágico. Para cambiar el código de byting de la clase, utilicé el Javassist de JBoss. Aunque no tiene que usarlo así, el Javassist de JBoss es realmente poderoso, lo que le permite cambiar fácilmente el código de byto de la clase.
En el método anterior, cambié el bytecode de la clase y agregué longtime = system.nanotime (); a la entrada del método de cada clase, y agregó System.out.println ("MethodClassName.MethodName:"+(System.nanotime ()-STEME));
Gracias por leer, espero que pueda ayudarte. ¡Gracias por su apoyo para este sitio!