如何在Java中更改CLASSPATH?

2022-08-31 16:11:51

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


在你问我“你为什么要这样做?我很快就会解释一下。

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

这就是原因,但我不希望这个问题被标记为某种奇怪的语言,而不是被大多数可能知道答案的Java开发人员所忽视。


答案 1

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

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

我刚才描述的类装入策略是用新类型实现的,在 Java 9 中,应用程序类装入器是该类型的。
这意味着它不再是,因此偶尔或序列将不再执行。URLClassLoader(URLClassLoader) getClass().getClassLoader()(URLClassLoader) ClassLoader.getSystemClassLoader()

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


对于 Java 8 或更低版本:

一些一般性评论:

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

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

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

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 来说可能并非如此),那么您也可以使用反射来实际修改系统类路径...(但这是一个黑客;)):

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");

答案 2

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


推荐