2009-06-18 3 views
58

È possibile aggiungere un file (non necessariamente un file jar) a classpath java in fase di esecuzione. In particolare, il file è già presente nel classpath, quello che voglio è se posso aggiungere una copia modificata di questo file al classpath.Aggiunta di file a classpath java in fase di esecuzione

Grazie,

+0

È lì e un modo per rimuovere un file jar? Oppure sostituirlo con una versione più recente? – dmarrazzo

risposta

0

sì, è possibile. dovrà essere nella sua struttura del pacchetto in una directory separata dal resto del codice compilato se si desidera isolarlo. quindi metti la sua dir base nella parte anteriore del classpath sulla riga di comando.

0

Sì, credo sia possibile ma potrebbe essere necessario implementare il proprio classloader. Non l'ho mai fatto ma questa è la strada che probabilmente guarderei.

5

È possibile provare java.net.URLClassloader con l'url della cartella/jar in cui risiede la classe aggiornata e utilizzarla invece del classloader predefinito durante la creazione di un nuovo thread.

37

È possibile aggiungere solo cartelle o file jar a un caricatore di classi. Quindi se hai un singolo file di classe, devi prima metterlo nella struttura di cartelle appropriata.

Here è un piuttosto brutto hack che si aggiunge alla SystemClassLoader in fase di esecuzione:

import java.io.IOException; 
import java.io.File; 
import java.net.URLClassLoader; 
import java.net.URL; 
import java.lang.reflect.Method; 

public class ClassPathHacker { 

private static final Class[] parameters = new Class[]{URL.class}; 

public static void addFile(String s) throws IOException { 
    File f = new File(s); 
    addFile(f); 
}//end method 

public static void addFile(File f) throws IOException { 
    addURL(f.toURL()); 
}//end method 


public static void addURL(URL u) throws IOException { 

    URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 
    Class sysclass = URLClassLoader.class; 

    try { 
    Method method = sysclass.getDeclaredMethod("addURL", parameters); 
    method.setAccessible(true); 
    method.invoke(sysloader, new Object[]{u}); 
    } catch (Throwable t) { 
    t.printStackTrace(); 
    throw new IOException("Error, could not add URL to system classloader"); 
    }//end try catch 

    }//end method 

}//end class 

La riflessione è necessaria per accedere al metodo protetto addURL. Questo potrebbe fallire se c'è un SecurityManager.

+0

Grazie per la risposta ragazzi, immagino che possiamo anche aggiungere un file semplice (non una classe o un barattolo ecc ...) nel classpath, ma come faccio a sapere quale verrà prelevato (l'ultima aggiunta è il classpath o il vecchio file) –

+0

Ah, questo è un problema. Gran parte di questo dipende dall'implementazione, quindi non dovresti davvero avere classi (o altre risorse) con lo stesso nome nello stesso classloader. – Thilo

+0

Se li metti in classloader separati, allora c'è una specifica. Di solito il classloader genitore prende la precedenza, per le webapp a volte è il contrario (ma ancora ben definito). Il bootloader viene sempre per primo. – Thilo

32

Prova questo uno per dimensioni.

private static void addSoftwareLibrary(File file) throws Exception { 
    Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); 
    method.setAccessible(true); 
    method.invoke(ClassLoader.getSystemClassLoader(), new Object[]{file.toURI().toURL()}); 
} 

Questo modifica il caricatore classe di sistema per includere il contenitore di libreria specificato. È piuttosto brutto, ma funziona.

+0

Ottima risposta.Vorrei comunque modificarlo un po ', facendo uso di vararg per mantenerlo ancora più semplice. –

12

Il modo in cui ho fatto questo è quello di utilizzare la mia stessa classe loader

URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 
DynamicURLClassLoader dynalLoader = new DynamicURLClassLoader(urlClassLoader); 

e creare la seguente classe:

public class DynamicURLClassLoader extends URLClassLoader{ 

public DynamicURLClassLoader(URLClassLoader classLoader) { 
    super(classLoader.getURLs()); 

} 

@Override 
public void addURL(URL url) { 
    super.addURL(url); 
} 

} 

funziona senza alcun riflesso

+0

Questa soluzione mi sembrava buona, ma non funziona (forse perché sono in una webapp Tomcat?). Ottengo un errore di "classe non trovata" quando provo a chiamare un metodo da una classe contenuta nel JAR caricato dinamico. Le altre soluzioni funzionano, anche in una webapp. – toni07

+0

Questo non funziona poiché si crea un nuovo classloader, gli altri classloader non accedono al file aggiunto a questo classloader – cinqS

-2

La mia soluzione:

File jarToAdd = new File("/path/to/file"); 

new URLClassLoader(((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs()) { 
    @Override 
    public void addURL(URL url) { 
     super.addURL(url); 
    } 
}.addURL(jarToAdd.toURI().toURL()); 
+1

Spiegare cosa fa il codice (__in risposta stessa, non i commenti__) oltre a fornirlo . –

+0

Questo non funziona perché il metodo 'addURL' in' URLClassLoader' è 'protected'. Ma puoi scrivere il tuo URLClassLoader che riscrive il metodo e il modificatore dovrebbe essere accessibile. – cinqS