可以在运行时将目录添加到类路径吗?

Ced*_*tin 33 java classpath

为了更好地理解Java中的工作原理,我想知道我是否可以在运行时动态地将目录添加到类路径中.

例如,如果我使用"java -jar mycp.jar"启动.jar并输出java.class.path属性,我可能会得到:

java.class.path: '.:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java'
Run Code Online (Sandbox Code Playgroud)

现在可以在运行时修改此类路径以添加另一个目录吗?(例如在使用位于我想添加的目录中的.jar对类进行第一次调用之前).

Jon*_*ner 48

您可以使用以下方法:

URLClassLoader.addURL(URL url)
Run Code Online (Sandbox Code Playgroud)

但是你需要用反射做这个,因为方法是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});
}
Run Code Online (Sandbox Code Playgroud)

请参阅反射 Java Trail .特别是反射的缺点

  • 似乎 URLClassLoader 的转换在 Java 9 中被破坏(https://community.oracle.com/thread/4011800 等) (2认同)

knb*_*knb 23

更新2014:这是来自2011年Jonathan Spooner接受的答案中的代码,稍作重写,让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()});
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为编辑接受的答案会更加困难.没有更多重复的答案,这就是我们在这个网站上的喜好. (4认同)
  • @Zeemee我几乎不记得了,也许我认为这是一个边缘案例.我只是决定过多地避免接受的答案; 我不想超越作者的意图. (2认同)

Kas*_*yap 12

是的,你可以使用URLClassLoader..见这里的例子.不使用反射.

- 编辑 -

根据建议从链接复制示例.

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这很好,这可以避免反射但它需要付出代价:你不是在你的进程类路径中添加一个路径,而是创建一个包含新路径的新类加载器并在具体线程中使用它,在本例中是你当前的线程.这意味着以这种方式添加的任何路径只会在具有显式修改的类加载器的那些线程上注明. (3认同)