Red de conocimiento informático - Material del sitio web - Cómo usar javassist para modificar métodos de clases cargadas

Cómo usar javassist para modificar métodos de clases cargadas

Manual de referencia:

1. Leer y generar código de bytes

ClassPool pool = ClassPool.getDefault();

// Se buscará desde la ruta de clases Class

CtClass cc = pool.get("test.writeFile("c://");

// La salida es formato binario

/ Byte [] b=cc.toBytecode();

// Genera y carga la biblioteca de clases. De forma predeterminada, se carga en el ClassLoader del hilo actual. También puede seleccionar la salida del ClassLoader. p>

/ /Class clazz=cc.toClass();

Aquí puede ver que la carga de Javassist depende de la clase ClassPool y el método de salida admite tres tipos.

2. addClass

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.makeClass("Punto");

// addMethod

cc. addMethod(m);

/Nuevo campo

cc.addField(f);

3.addField( f);

De lo anterior podemos ver que la modificación de Class depende principalmente de la clase CtClass

3. Congelar clase

Cuando CtClass llama a writeFile. (), toClass(), toBytecode(), Javassist congelará el objeto CtClass y no permitirá la modificación del objeto CtClass. Esto es principalmente para advertir al desarrollador que la clase se ha cargado y la JVM no permitirá que la clase se cargue. recargado Para deshacerse de esta restricción, haga lo siguiente:

CtClasss cc = ...;

:

cc.writeFile();

cc.defrost();

cc.setSuperclass(...)

Cuando ClassPool.doPruning=true, Javassist liberará los datos almacenados en el ClassPool correspondiente archivo. Esto puede reducir el consumo de memoria de javassist de forma predeterminada, ClassPool.doPruning=false. Por ejemplo:

CtClasss cc = ...;

cc.stopPruning(true);

:

cc.writeFile( ) ; // Convertir a archivo de clase.

// cc no se publicará

Consejo: al depurar, puede llamar a debugWriteFile (), lo que no provocará que se publique CtClass.

4. Ruta de búsqueda de clases

Como se puede ver en lo anterior, la carga de clases depende de ClassPool, y el método ClassPool.getDefault () para buscar Classpath solo busca lo mismo. ruta de la clase JVM. Cuando el programa se ejecuta en JBoss o Tomcat, el objeto ClassPool puede estar ejecutándose en JBoss, o en Tomcat, es posible que el objeto ClassPool no esté disponible.

Javassist proporciona cuatro métodos para cargar classpath dinámicamente. Los siguientes son

//El método de carga predeterminado, como pool.insertClassPath(new ClassClassPath(this.getClass()));

ClassPool pool = ClassPool.getDefault();

Método de carga dinámica del objeto ClassPool. getDefault(); // Cargar classpath desde el archivo

pool.insertClassPath("/usr/local/javalib")

Cargar desde URL

ClassPath cp = new URLClassPath("www.org", 80, "/java/", "org.javassist.");

pool.insertClassPath(cp); palabra Sección[]Cargando

byte[] b = una matriz de bytes;

Nombre de cadena = nombre de clase;

cp.insertClassPath(new ByteArrayClassPath(nombre, b));

InputStream ins = flujo de entrada para leer archivos de clase;

CtClass cc = cp.makeClass(ins);

5.ClassPool< / p>

5.1 Reducir el desbordamiento de memoria

ClassPool es el contenedor de carga para objetos CtClass. ClassPool no libera un objeto CtClass cuando se carga (de forma predeterminada). Esto se debe a que el objeto CtClass puede usarse en la siguiente fase.

Si se cargan demasiados objetos CtClass, pueden producirse excepciones OutOfMemory. Para evitar esta excepción, javassist proporciona varios métodos, uno es el parámetro ClassPool.doPruning mencionado anteriormente y el otro es llamar al método CtClass.detach (), que puede eliminar el objeto CtClass de ClassPool. Por ejemplo:

CtClass cc = ...

cc.writeFile()

cc.detach(); Otra forma es no utilizar el ClassPool predeterminado, es decir, no utilizar ClassPool.getDefault() para generarlo. De esta manera, cuando no se hace referencia a ClassPool, la recolección de basura de la JVM recolectará la clase. Por ejemplo

//ClassPool(true) carga el ClassPath de la JVM de forma predeterminada

ClassPool cp = new ClassPool(true);

// Si es necesario, puede pasar appendClassPath ()

5.2 ClassPools en cascada

javassist admite ClassPools en cascada, que es similar a la herencia.

Por ejemplo:

ClassPool padre = ClassPool.getDefault();

ClassPool hijo = new ClassPool(padre

child.insertClassPath(". /classes); " );

5.2 ClassPool en cascada

javassist admite ClassPool en cascada, que es similar a la herencia. 3 Modifique el nombre de una clase existente para crear una nueva clase

Cuando se llama al método setName, modificará directamente el nombre de la clase existente. Si se usa nuevamente el nombre de la clase anterior, lo hará. recargarse en el classpath Down. get("Point");

Para objetos CtClass congelados, el nombre de la clase no se puede cambiar. Si es necesario cambiarlo, se puede volver a cargar, por ejemplo:

Grupo ClassPool. = ClassPool.getDefault()

CtClass cc = pool.get("Punto");

cc.writeFile(); setName("Pair") ; Dado que se ha llamado a writeFile(), es incorrecto. getAndRename("Point", "Pair");

6. Cargador de clases

Como se mencionó anteriormente, javassist no puede cargar la misma clase dos veces en el mismo ClassLoader. Por lo tanto, debe prestar atención a la salida de CtClass, por ejemplo:

// Cuando Hello no está cargado, puede ejecutar el siguiente comando.

ClassPool cp = ClassPool.getDefault();

CtClass cc = cp.get("Hola"

Clase c = cc.toClass()); ;

// En este caso, dado que se ha cargado Hello2, no hay problema con las siguientes operaciones. Entonces ocurrirá el error

Hello2 h=new Hello2();

CtClass cc2 = cp.get("Hello2");

Class c2 = cc . toClass(); // Aquí se producirá una excepción java.lang.LinkageError.

Se generará una excepción LinkageError

//Resuelva el problema de carga especificando un ClassLoader descargado

Class c3 = cc.toClass(new MyClassLoader());

6.1 Usar javassist.Loader

Clase c3 = cc.toClass(new MyClassLoader());

6.1.Loader

De lo anterior, podemos ver Si carga ClassLoader dos veces en el mismo ClassLoader, se generará una excepción. Por conveniencia, javassist también proporciona un Classloader para su uso, por ejemplo

ClassPool pool = ClassPool.newInstance();

.

:

Para monitorear más convenientemente el ciclo de vida del ClassLoader que viene con Javassist, javassist también proporciona un oyente para monitorear el ciclo de vida del ClassLoader, por ejemplo:

//Traductor para el oyente

clase pública MyTranslator implementa Translator {

void start(ClassPool pool)

lanza NotFoundException, CannotCompileException {}

void onLoad(grupo ClassPool, nombre de clase String)

lanza NotFoundException, CannotCompileException

lanza NotFoundException, CannotCompileException

{

CtClass cc = pool . get(nombre de clase);

cc.setModifiers(Modifier.PUBLIC);

}

}

}

.

//Ejemplo

clase pública Main2 {

public static void main(String[] args) throws Throwable {

Traductor t = new MyTranslator( ) ;

ClassPool pool = ClassPool.getDefault();}

}

//Salida

java Main2 arg1 arg2..

6.2 Modificar clases del sistema

Se puede ver en la especificación JVM que el cargador de clases del sistema tiene prioridad sobre otros cargadores de clases, y el cargador de clases del sistema carga principalmente clases del sistema. es necesario modificar la clase del sistema, si los parámetros predeterminados al ejecutar el programa no se pueden modificar. Si ejecuta el programa con parámetros predeterminados, no se pueden modificar. Si necesita modificar las clases del sistema, hay formas de hacerlo, que es agregar -Xbootclasspath/p al ejecutar el programa: el significado de este parámetro se puede encontrar en otra documentación.

El siguiente es un ejemplo de modificación de String:

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("java.lang.String");

CtField f = new CtField(CtClass.intType, "hiddenValue"); m.insertBefore("{ System.out.println($1); System.out.println($2); }");

Nota: Si javassist cambia el valor de $1, el valor real del parámetro también cambiará.

7.1.2 $args

$args se refiere a una matriz que contiene todos los parámetros del método, similar a Object[]. Si el parámetro contiene un tipo básico, la matriz será. convertido a su tipo de paquete. Tenga en cuenta que $args[0] corresponde a $1 en lugar de $0, $0!=$args[0], $0=this.

7.1.3 $$

$ es la abreviatura de todos los parámetros del método y se utiliza principalmente para llamadas a métodos. Por ejemplo:

//Método original

move(String a, String b)

move($$) es equivalente a move($1, $2)

p>

Si agrega un nuevo método y el método contiene todos los parámetros de movimiento, puede escribir:

exMove($$, contexto) es equivalente a exMove($1 , $2, contexto)

7.1.4 $cflow

7.1.4 $cflow

7.1.5 $cflow1.4 $cflow

$ cflow, que significa flujo de control, es una variable de solo lectura cuyo valor es la profundidad de la llamada al método. Por ejemplo:

//Método original

int fact(int n) {

if (n lt; = 1)

return n;

else

return n * fact(n - 1);

}

12 importar