2009-11-23 6 views
23

Si se ha serializado todo el archivo .class en byte [], y suponiendo que se conoce el nombre de la clase (pasado junto con el byte []), cómo se convierte byte [] -> Class -> luego se carga a la JVM para que luego pueda usarlo llamando a Class.forName()?Java: ¿Cómo cargar la clase almacenada como byte [] en la JVM?

NOTA: que estoy haciendo esto porque me envió el .class a otro host, y JVM del anfitrión no sabe nada de este .class.

Respuesta

25

En realidad estoy usando algo como esto ahora en una prueba para dar un conjunto de definiciones de clases como byte [] a un cargador de clases:

public static class ByteClassLoader extends URLClassLoader { 
    private final Map<String, byte[]> extraClassDefs; 

    public ByteClassLoader(URL[] urls, ClassLoader parent, Map<String, byte[]> extraClassDefs) { 
     super(urls, parent); 
     this.extraClassDefs = new HashMap<String, byte[]>(extraClassDefs); 
    } 

    @Override 
    protected Class<?> findClass(final String name) throws ClassNotFoundException { 
     byte[] classBytes = this.extraClassDefs.remove(name); 
     if (classBytes != null) { 
     return defineClass(name, classBytes, 0, classBytes.length); 
     } 
     return super.findClass(name); 
    } 

    } 
+0

Alex, pero ¿y si 2 .class dependen el uno del otro? ¿Seguiría funcionando? – sivabudh

+0

Probablemente no. Probablemente deberías estar enviando (por ejemplo) un archivo JAR, no una secuencia de archivos .class separados. –

+3

@ ShaChris23 - seguro, ¿por qué no? @Stephen C: parece una visión limitada de lo que pueden hacer los cargadores de clases Java. ¿Por qué dices probablemente no? –

4

Extienda ClassLoader y luego implemente la llamada necesaria para obtener su byte[] dentro del método defineClass(). Por el contrario, puede hacer esto en findClass(). De este modo, cargará este ClassLoader al principio o lo empleará cuando necesite definiciones de clase a través de su matriz.

public class ByteArrayClassLoader extends ClassLoader { 

    public Class findClass(String name) { 
     byte[] ba = /* go obtain your byte array by the name */; 

     return defineClass(name,ba,0,ba.length); 
    } 

} 
+0

¿No necesita llamar también a resolveClass()? – sivabudh

9

Uso del (int, String, byte [], int) método defineClass de ClassLoader

1

bien, así que aquí es lo que hice:

public class AgentClassLoader extends ClassLoader 
{ 
    public void loadThisClass(ClassByte classByte_) 
    { 
    resolveClass(defineClass(classByte_.getName(), 
          classByte_.getBytes(), 
          0, 
          classByte_.getBytes().length)); 
    } 
} 

espero que carga la clase en JVM? Si esto realmente funciona, ¿por qué el implementador Java no definió los métodos públicos defineClass y resolveClass? Realmente me pregunto qué estaban pensando. ¿Alguien puede iluminarme?

simplemente para la corrección, aquí está ClassByte

import java.io.Serializable; 

public class ClassByte implements Serializable 
{ 
    private String name; 
    private byte[] bytes; 

    ClassByte(String name_, byte[] bytes_) 
    { 
    name = name_; 
    bytes = bytes_; 
    } 

    public String getName() 
    { 
    return name; 
    } 

    public byte[] getBytes() 
    { 
    return bytes; 
    } 
} 
+1

Creo que la idea es evitar que código aleatorio invoque 'defineClass' en el sistema/cargadores de clases predeterminados. Esto tiene el potencial de comprometer la seguridad y, en general, suceden cosas "realmente extrañas". Es mejor solo permitir esto en cargadores de clases personalizados. –

0

La respuesta de Alex es correcta, pero si tiene una relación de herencia entre clases, primero debe asegurarse de cargar la clase principal. De lo contrario, el cargador de clases no podrá definirlo.

+0

¿De verdad? Creo que es un problema de vinculación más que un problema de definición de clase. No es necesario que las clases se definan en el orden correcto; de lo contrario, no podríamos tener dependencias circulares en las clases. Por ejemplo, Class.getName() devuelve una Cadena pero String.getClass() devuelve una Clase. Además, ambas clases tienen un padre de Object pero Object.toString() y Object.getClass() devuelven String y Class, respectivamente. Si lo que dices fuera cierto no habría un orden correcto para cargar estas clases. – Kidburla

+0

@Kidburla, eso se debe a que la JVM carga las clases cuando las necesita, de modo que puede cargar la clase sin necesidad de cadenas cargadas, y la cadena se cargará cuando llame a 'getName'. Sin embargo, las superclases/interfaces deben cargarse con o antes de que se cargue la clase, de lo contrario, la clase está incompleta. Es por eso que no podemos tener herencia circular, porque los cargadores de clase no saben cómo lidiar con eso. –

+0

Es cierto.Lo siento, leí mal "herencia" como "dependiente" debido a los comentarios sobre la respuesta aceptada. – Kidburla

Cuestiones relacionadas