Java在运行时添加类路径
这个问题的答案有很多,在《史塔克溢出》一书中? 但是最常用的方法是将ClassLoader.getSystemClassLoader转换为URLClassLoader,这样就不再有效了 类必须由systemclassloader找到 还有别的解决办法吗? -不重新启动jar -在这种情况下,如果不创建自己的类加载器,我必须用自己的类加载器替换systemclassloader 缺少的类/JAR必须在启动时添加,我不想在带有类路径的清单中添加它们。 我找到了带有premain方法的Java代理。这也可以很好地工作,但在本例中,我希望在不调用java-javaagent的情况下启动premain方法:-罐子 目前,我从一开始就重新启动程序,但缺少类路径:Java在运行时添加类路径,java,runtime,classpath,Java,Runtime,Classpath,这个问题的答案有很多,在《史塔克溢出》一书中? 但是最常用的方法是将ClassLoader.getSystemClassLoader转换为URLClassLoader,这样就不再有效了 类必须由systemclassloader找到 还有别的解决办法吗? -不重新启动jar -在这种情况下,如果不创建自己的类加载器,我必须用自己的类加载器替换systemclassloader 缺少的类/JAR必须在启动时添加,我不想在带有类路径的清单中添加它们。 我找到了带有premain方法的Java代理。这
public class LibLoader {
protected static List<File> files = new LinkedList<>();
public static void add(File file) {
files.add(file);
}
public static boolean containsLibraries() {
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
String[] classpaths = runtimeMxBean.getClassPath().split(System.getProperty("path.separator"));
List<File> classpathfiles = new LinkedList<>();
for(String string : classpaths) classpathfiles.add(new File(string));
for(File file : files) {
if(!classpathfiles.contains(file)) return false;
}
return true;
}
public static String getNewClassPaths() {
StringBuilder builder = new StringBuilder();
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
builder.append(runtimeMxBean.getClassPath());
for(File file : files) {
if(builder.length() > 0) builder.append(System.getProperty("path.separator"));
builder.append(file.getAbsolutePath());
}
return builder.toString();
}
public static boolean restartWithLibrary(Class<?> main, String[] args) throws IOException {
if(containsLibraries()) return false;
List<String> runc = new LinkedList<>();
runc.add(System.getProperty("java.home") + "\\bin\\javaw.exe");
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List<String> arguments = runtimeMxBean.getInputArguments();
runc.addAll(arguments);
File me = new File(LibLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath());
String classpaths = getNewClassPaths();
if(!classpaths.isEmpty()) {
runc.add("-cp");
runc.add(classpaths);
}
if(me.isFile()) {
runc.add("-jar");
runc.add(me.getAbsolutePath().replace("%20", " "));
} else {
runc.add(main.getName());
}
for(String arg : args) runc.add(arg);
ProcessBuilder processBuilder = new ProcessBuilder(runc);
processBuilder.directory(new File("."));
processBuilder.redirectOutput(Redirect.INHERIT);
processBuilder.redirectError(Redirect.INHERIT);
processBuilder.redirectInput(Redirect.INHERIT);
Process process = processBuilder.start();
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
}
希望有人有更好的解决方案
问题是,这些类必须在我的系统类加载器中找到,而不是由新的类加载器找到 听起来,当前重新启动JVM的解决方案是唯一干净的方法 无法更改系统类加载器,并且不能在运行时向其添加额外的JAR 如果您试图使用反射来扰乱系统类加载器的数据结构,那么它最多只能是不可移植的,并且依赖于版本。在最坏的情况下,它要么容易出错。。。或者被JVM的运行时安全机制阻止 约翰内斯·库恩在评论中提出的解决方案行不通。JVM引导过程中会参考java.system.class.loader属性。在应用程序运行时,对其进行更改将不会产生任何效果。我不相信他的方法也会奏效 这里有一个可能的替代方法来处理这个问题。。。如果你能尽早找到丢失的罐子 编写一个执行以下操作的启动器类: 保存命令行参数 查找应用程序JAR文件 从MANIFEST.MF中提取主类和类路径属性。 根据以上内容计算出真正的类路径应该是什么。。。以及其他特定于应用程序的逻辑。 使用正确的类路径创建一个新的URLClassLoader,并将系统类加载器作为其父类。 使用它来加载主类。 使用反射查找main方法中的main类。 通过save命令行参数调用它。
这本质上是Spring Bootstrap和OneJar以及其他处理jar中的jar问题的方法,等等。它避免启动2个虚拟机。这里有什么问题?你有什么错误?目前我没有收到任何错误。但是我想在运行时添加类路径,而不重新启动jar。要将systemclassloader强制转换为urlclassloader并添加URL不再工作,请创建一个新的classloader?问题是,这些类必须在systemclassloader中找到,而不是由新的classloader找到。所以我必须用我自己的来替换systemclassloader,我认为这不起作用。。。这显然是错误的。你有没有读过我的评论,并点击了链接?在过去的一周里,我确实更改了系统类加载器Java14,对于我的案例来说,它确实工作得很好。但是在JVM运行时,您没有这样做!这就是OP想要做的。是的,改变一辆正在行驶的汽车的轮子几乎是不可能的。F1仍然让赛车停下来做这件事。但是我不明白为什么你不想用正确的参数重新启动你的虚拟机一次,以便以后能够进行一些调整。这就是我回答的第一行所说的!是的,我读过你的评论和链接,除非你建议他写一个自定义的加载器来替换系统类加载器,它允许你动态更新类路径,否则这种方法在这里是行不通的。这对运行时类型的安全性有一些可怕的影响。在我看来,最好的解决方案是使用权限参数重新启动VM。感谢您的帮助: