60

如何从 Java 进程中更改 Java 进程的 CLASSPATH?


在你问我“你为什么要这么做?”之前 我会尽快解释。

当您运行 Clojure REPL 时,通常需要在 CLASSPATH 中使用更多 jar 来加载Clojure源文件,我想这样做而不必重新启动 Clojure 本身(在 Slime 上使用它时,这并不是一个真正的选择在 Emacs 上)。

这就是原因,但我不希望这个问题被标记为 some-weird-language some-weird-editor 并且被大多数可能有答案的 Java 开发人员忽视。

4

6 回答 6

80

2017 年第四季度更新:正如vda8888在下面评论的那样,在 Java 9 中,系统不再是.java.lang.ClassLoaderjava.net.URLClassLoader

请参阅“ Java 9 迁移指南:七个最常见的挑战

我刚刚描述的类加载策略是在一种新类型中实现的,而在 Java 9 中,应用程序类加载器就是这种类型。
这意味着它URLClassLoader不再是 a,因此偶尔(URLClassLoader) getClass().getClassLoader()(URLClassLoader) ClassLoader.getSystemClassLoader()序列将不再执行。

java.lang.ModuleLayer将是用于影响模块路径(而不是类路径)的替代方法。例如,参见“ Java 9 模块 - JPMS 基础知识”。


对于 Java 8 或更低版本:

一些一般性评论:

您不能(以保证工作的可移植方式,见下文)更改系统类路径。相反,您需要定义一个新的 ClassLoader。

类加载器以分层方式工作......因此,任何对类 X 进行静态引用的类都需要加载到与 X 相同的类加载器中,或者加载到子类加载器中。您不能使用任何自定义 ClassLoader 来使系统 ClassLoader 链接正确加载代码,如果以前没有这样做的话。因此,除了您找到的额外代码之外,您还需要安排在自定义 ClassLoader 中运行您的主要应用程序代码。
(话虽如此,评论中提到了这个扩展的 URLClassLoader例子)

您可能会考虑不编写自己的 ClassLoader,而只需使用 URLClassLoader。使用不在父类加载器 url 中的url 创建一个 URLClassLoader 。

URL[] url={new URL("file://foo")};
URLClassLoader loader = new URLClassLoader(url);

完整的解决方案是:

ClassLoader currentThreadClassLoader
 = Thread.currentThread().getContextClassLoader();

// Add the conf dir to the classpath
// Chain the current thread classloader
URLClassLoader urlClassLoader
 = new URLClassLoader(new URL[]{new File("mtFile").toURL()},
                      currentThreadClassLoader);

// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(urlClassLoader);

如果您假设 JVM 系统类加载器是 URLClassLoader(可能并非所有 JVM 都如此),您也可以使用反射来实际修改系统类路径......(但这是一个 hack;)):

public void addURL(URL url) throws Exception {
  URLClassLoader classLoader
         = (URLClassLoader) ClassLoader.getSystemClassLoader();
  Class clazz= URLClassLoader.class;

  // Use reflection
  Method method= clazz.getDeclaredMethod("addURL", new Class[] { URL.class });
  method.setAccessible(true);
  method.invoke(classLoader, new Object[] { url });
}

addURL(new File("conf").toURL());

// This should work now!
Thread.currentThread().getContextClassLoader().getResourceAsStream("context.xml");
于 2008-10-31T09:37:34.727 回答
3

我不相信你可以——正确的做法(我相信)是用新的路径创建一个新的类加载器。或者,您可以编写自己的类加载器,它允许您动态更改类路径(对于该加载器)。

于 2008-10-31T08:49:32.173 回答
2

无需编写自己的类加载器!有clojure.lang.DynamicClassLoader

http://blog.japila.pl/2011/01/dynamically-redefining-classpath-in-clojure-repl/

于 2011-07-20T11:57:47.000 回答
1

您可能想研究使用java.net.URLClassLoader。它允许您以编程方式加载最初不在类路径中的类,但我不确定这是否正是您需要的。

于 2008-10-31T09:36:38.537 回答
1

从下面的两个链接中可以看出,VonC 提供的方法似乎是最好的,但请查看其中一些帖子和谷歌的“Java Dynamic Classpath”或“Java Dynamic Class Loading”,并从那里找到一些信息。

我会更深入地发布,但 VonC 已经完成了这项工作。

来自类和 Jar 文件的动态加载

另请查看此sun 论坛帖子

于 2008-10-31T10:23:37.287 回答
-4
String s="java  -classpath abcd/ "+pgmname+" "+filename;   
Process pro2 = Runtime.getRuntime().exec(s); 
BufferedReader in = new BufferedReader(new InputStreamReader(pro2.getInputStream()));

is an example of changin the classpath in java program

于 2013-10-10T10:17:00.250 回答