2011-10-25 4 views
23

Per capire meglio come funzionano le cose in Java, mi piacerebbe sapere se posso aggiungere dinamicamente, in runtime, una directory al percorso della classe.È possibile aggiungere una directory al percorso della classe in fase di runtime?

Per esempio, se io lancio un .jar utilizzando "java-jar mycp.jar" e l'uscita del java.class.path proprietà, io possa avere:

java.class.path: '.:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java' 

Ora posso modificare questo percorso di classe in fase di runtime per aggiungere un'altra directory? (ad esempio, prima di effettuare la prima chiamata a un corso utilizzando uno .jar situato in quella directory che voglio aggiungere).

risposta

38

È possibile utilizzare il seguente metodo:

URLClassLoader.addURL(URL url) 

ma avrete bisogno di fare questo con la riflessione dal momento che il metodo è protected:

public static void addPath(String s) throws Exception { 
    File f = new File(s); 
    URL u = f.toURL(); 
    URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 
    Class urlClass = URLClassLoader.class; 
    Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class}); 
    method.setAccessible(true); 
    method.invoke(urlClassLoader, new Object[]{u}); 
} 

vedere la traccia Java su Reflection . In particolare la sezione Inconvenienti di riflessione

+0

Davvero molto utile, grazie! –

+0

https://docs.oracle.com/javase/7/docs/api/java/net/URLClassLoader.html –

+0

questo non funziona formami .. non sono in grado di caricare la classe dopo aver usato Class.forName (" com.mysql.jdbc.Driver "); –

9

Sì, è possibile utilizzare URLClassLoader .. vedere l'esempio here. Non usa la riflessione.

- modifica -

Copia esempio dal link come suggerito.

import javax.naming.*; 
import java.util.Hashtable; 
import java.net.URLClassLoader; 
import java.net.URL; 
import java.net.MalformedURLException; 

public class ChangeLoader { 

    public static void main(String[] args) throws MalformedURLException { 
    if (args.length != 1) { 
     System.err.println("usage: java ChangeLoader codebase_url"); 
     System.exit(-1); 
    } 

    String url = args[0]; 
    ClassLoader prevCl = Thread.currentThread().getContextClassLoader(); 

    // Create class loader using given codebase 
    // Use prevCl as parent to maintain current visibility 
    ClassLoader urlCl = URLClassLoader.newInstance(new URL[]{new URL(url)}, prevCl); 

     try { 
     // Save class loader so that we can restore later 
      Thread.currentThread().setContextClassLoader(urlCl); 

     // Expect that environment properties are in 
     // application resource file found at "url" 
     Context ctx = new InitialContext(); 

     System.out.println(ctx.lookup("tutorial/report.txt")); 

     // Close context when no longer needed 
     ctx.close(); 
    } catch (NamingException e) { 
     e.printStackTrace(); 
     } finally { 
      // Restore 
      Thread.currentThread().setContextClassLoader(prevCl); 
     } 
    } 
} 
+0

L'esempio ci sono buoni ed evitano l'uso del riflesso. Ma forse puoi copiarli qui - nel caso in cui il link si rompa? –

+0

@AlexeyGrigorev fatto. – Kashyap

+3

È fantastico che questo eviti il ​​riflesso ma ha un prezzo: non stai aggiungendo un percorso al classpath del processo ma creando un nuovo programma di caricamento classi che include il nuovo percorso e lo usa all'interno di un thread concreto, in questo caso, il tuo attuale filo. Ciò significa che qualsiasi percorso aggiunto in questo modo verrà notato solo su quei thread con programmi di caricamento classe esplicitamente modificati. –

20

Aggiornamento 2014: questo è il codice della risposta accettata, da Jonathan Spooner a partire dal 2011, un po 'riscritto per avere validatori di Eclipse non creano più avvertenze (deprecazione, rawtypes)

//need to do add path to Classpath with reflection since the URLClassLoader.addURL(URL url) method is protected: 
public static void addPath(String s) throws Exception { 
    File f = new File(s); 
    URI u = f.toURI(); 
    URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); 
    Class<URLClassLoader> urlClass = URLClassLoader.class; 
    Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class}); 
    method.setAccessible(true); 
    method.invoke(urlClassLoader, new Object[]{u.toURL()}); 
} 
+2

Sarebbe stato più stackoverflowish modificare la risposta accettata, penso. Non ci sono più risposte doppie, questo è ciò che ci piace in questo sito. – Zeemee

+2

@Zeemee Ricordo a malapena, forse ho pensato che fosse un caso limite. Ho semplicemente deciso di evitare troppo la risposta accettata; Non volevo andare al di là delle intenzioni dell'autore. – knb