2011-10-25 33 views
23

爲了更好地理解Java中的工作方式,我想知道是否可以在運行時動態添加一個目錄到類路徑。可以在運行時將目錄添加到類路徑中嗎?

例如,如果我發動的.jar使用「Java的罐子mycp.jar」和輸出java.class.path財產,我可以得到:

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

現在我可以在運行時修改這個類路徑來添加另一個目錄嗎? (例如在使用位於我想要添加的目錄中的.jar進行第一次類的調用之前)。

回答

38

您可以使用下面的方法:

URLClassLoader.addURL(URL url) 

但是,你需要使用反射來做到這一點,因爲該方法是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}); 
} 

參見Reflection在Java步道。尤其是部分反射的缺點

+0

真的很有用,謝謝! –

+0

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

+0

這不是工作形式我..我不能加載類之後使用Class.forName(「 com.mysql.jdbc.Driver「); –

9

是的,您可以使用URLClassLoader ..參見示例here。不使用反射。

- 編輯 -

從建議的鏈接複製示例。

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

這個例子很好,他們避免使用反射。但是,也許你可以在這裏複製它們 - 以防鏈接被破壞? –

+0

@AlexeyGrigorev完成。 – Kashyap

+3

這很好,它避免了反射,但它的價格很高:您不會爲流程類路徑添加路徑,而是會創建一個新的類加載器,其中包含新路徑並在具體線程中使用它,在這種情況下,您的當前線。這意味着以這種方式添加的任何路徑只會在具有明確修改的類加載器的那些線程中註明。 –

20

更新2014:這是公認的答案,從2011碼,由喬納森·斯普納,稍微改寫使Eclipse的驗證程序不再創建警告(折舊,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

我認爲,編輯接受的答案本來是更加堆疊式的。沒有更多重複的答案,這就是我們在這個網站上喜歡的。 – Zeemee

+2

@Zeemee我幾乎不記得,也許我認爲這是一個邊緣案例。我只是決定避免接受的答案太多;我不想超越作者的意圖。 – knb

相關問題