2009-11-23 1 views
23

Se uno ha serializzato l'intero file .class in byte [] e assumendo che il nome della classe sia noto (passato insieme al byte []), come si converte byte [] -> Classe -> quindi caricarlo alla JVM in modo che potrei in seguito usarlo chiamando Class.forName()?Java: come caricare la classe memorizzata come byte [] nella JVM?

NOTA: sto facendo questo perché ho inviato il .class su un altro host e JVM del padrone di casa non sapere su questo .class.

risposta

25

realtà sto usando qualcosa di simile in questo momento in un test per dare una serie di definizioni di classe come byte [] per un ClassLoader:

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, ma cosa succede se 2 .class dipendono l'uno dall'altro? funzionerebbe ancora? – sivabudh

+0

Probabilmente no. Probabilmente dovresti inviare (per esempio) un file JAR non una sequenza di file .class separati. –

+3

@ ShaChris23 - certo, perché non dovrebbe? @Stephen C: sembra una visione limitata di ciò che i classloader Java possono fare. Perché dici probabilmente no? –

4

Estendere ClassLoader e quindi implementare la chiamata necessaria per ottenere il byte[] all'interno del metodo defineClass(). Al contrario, puoi farlo in findClass(). Caricherai quindi questo ClassLoader all'inizio o lo utilizzerai quando avrai bisogno di definizioni di classe tramite l'array.

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

non è necessario chiamare resolClass()? – sivabudh

9

Utilizzare il (int String, byte [],, int) Metodo defineClass da ClassLoader

1

Bene, ecco quello che ho fatto:

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

Spero che carica la classe in JVM? Se funziona davvero, perché l'implementatore Java non ha reso entrambi i metodi pubblici defineClass e resolveClass? Mi chiedo davvero cosa stessero pensando. Chiunque può illuminarmi?

Solo per completezza, ecco 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

Penso che l'idea sia quella di scoraggiare il codice casuale dal chiamare 'defineClass' sul sistema/programma di caricamento predefinito della classe. Questo ha il potenziale per compromettere la sicurezza e in genere rendere ** cose veramente strane ** accadono. È meglio consentirlo solo su classloader personalizzati. –

0

La risposta di Alex è corretta ma se si ha una relazione di ereditarietà tra classi è necessario assicurarsi di caricare prima la classe genitore. In caso contrario, il programma di caricamento classi non sarà in grado di definirlo.

+0

Davvero? Penso che sia un problema di collegamento piuttosto che un problema di definizione di classe. Le classi non devono essere definite nell'ordine corretto, altrimenti non potremmo avere dipendenze circolari nelle classi. Ad esempio, Class.getName() restituisce una stringa ma String.getClass() restituisce una classe. Inoltre, entrambe le classi hanno un oggetto principale di Object ma Object.toString() e Object.getClass() restituiscono rispettivamente String e Class. Se quello che stavi dicendo fosse vero, non ci sarebbe alcun ordine corretto per caricare queste classi. – Kidburla

+0

@Kidburla perché la JVM esegue il caricamento per le classi quando sono necessarie, quindi è possibile caricare la classe senza che sia necessario caricare la stringa e String verrà caricato quando si chiama 'getName'. Tuttavia, le super-classi/interfacce devono essere caricate con o prima che la classe venga caricata, altrimenti la classe è incompleta. Ecco perché non possiamo avere un'ereditarietà circolare, perché i programmi di caricamento di classe non sanno come gestirlo. –

+0

Vero.Spiacente, ho interpretato erroneamente "ereditarietà" come "dipendente" a causa dei commenti sulla risposta accettata. – Kidburla