如何从 Java 进程内部更改 Java 进程的 CLASSPATH?
在你问我“你为什么要这么做之前?”我很快就会解释一下。
当您运行 Clojure REPL 时,通常需要在 CLASSPATH 中添加更多 jar 来加载 Clojure 源文件,并且我希望无需重新启动 Clojure 本身即可完成此操作(这在使用时并不是一个真正的选项)它在 Emacs 上的 Slime 上)。
这就是原因,但我不希望这个问题被标记为某种奇怪的语言、某种奇怪的编辑器,并被大多数可能有答案的 Java 开发人员忽视。
2023 年更新:如下面 Holger 评论
将 jar 文件添加到 Java 9 及更高版本中工作的类路径的唯一方法是通过 Instrumentation API,但它需要 Java 代理。
如果您控制启动器/主 jar,则可以使用 jar 文件清单中的
属性来启动嵌入式代理。Launcher-Agent-Class
2017 年第 4 季度更新:正如下面 vda8888 评论,在 Java 9 中,系统
java.lang.ClassLoader
不再是 java.net.URLClassLoader
。
请参阅“Java 9 迁移指南:七个最常见的挑战”
我刚才描述的类加载策略是用一种新类型实现的,在 Java 9 中应用程序类加载器就是这种类型。
这意味着它不再是,因此偶尔的URLClassLoader
或(URLClassLoader) getClass().getClassLoader()
序列将不再执行。(URLClassLoader) ClassLoader.getSystemClassLoader()
java.lang.ModuleLayer 将是一种替代方法,用于影响 modulepath(而不是类路径)。例如,请参阅“Java 9 模块 - JPMS 基础知识”。
对于 Java 8 或更低版本:
一些一般性评论:
您无法(以保证有效的可移植方式,见下文)更改系统类路径。相反,您需要定义一个新的类加载器。
类加载器以分层方式工作...因此任何对类 X 进行静态引用的类都需要加载到与 X 相同的类加载器中,或者加载到子类加载器中。您不能使用任何自定义类加载器来使系统类加载器链接正确加载代码(如果以前没有这样做的话)。因此,除了您找到的额外代码之外,您还需要安排主应用程序代码在自定义类加载器中运行。
(话虽这么说,cracked-all在评论中提到了扩展
URLClassLoader
的示例)
您可能会考虑不编写自己的ClassLoader,而只使用URLClassLoader。创建一个 URLClassLoader,其 url not 位于父类加载器 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),您也可以使用反射来实际修改系统类路径...(但这是一个 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");
我不相信你可以 - 正确的做法(我相信)是使用新路径创建一个新的类加载器。或者,您可以编写自己的类加载器,它允许您动态更改(该加载器的)类路径。
您可能想考虑使用 java.net.URLClassLoader。它允许您以编程方式加载最初不在类路径中的类,尽管我不确定这是否正是您所需要的。
从下面的两个链接中可以看出,VonC 提供的方法似乎是最好的,但请查看其中一些帖子并谷歌搜索“Java Dynamic Classpath”或“Java Dynamic Class Loading”并从中找到一些信息.
我会发布更深入的内容,但 VonC 几乎完成了这项工作。
来自类和Jar文件的动态加载。
另请查看此sun 论坛帖子。
String s="java -classpath abcd/ "+pgmname+" "+filename;
Process pro2 = Runtime.getRuntime().exec(s);
BufferedReader in = new BufferedReader(new InputStreamReader(pro2.getInputStream()));
是在java程序中更改类路径的示例