我必须同意关于这个问题的评论。根据您提供的代码,您收到错误似乎是由于 JAR 文件不在您期望的位置。正如@Andrew 所提到的,您没有在 addJarToClasspath 方法中检查文件是否存在。因此,如果该文件不存在,您将收到您所看到的 ClassNotFound 异常。我通过获取您的 ClassLoader 逻辑并向其传递有效和无效的 JAR 来验证此问题。当提供有效的 JAR/路径时,类加载器将按预期加载该类。当指定了无效的 JAR/路径时,我收到了您提到的错误。如果指定的 URL 不指向有效文件,则 URLClassLoader 不会引发异常。
要验证该场景,请打印出文件的完整路径,并查看它对于执行环境是否正确。
Edit
It appears that even if you override the system ClassLoader, the VM will still use the default
sun.misc.Launcher$AppClassLoader
to load some classes. In my testing this includes the classes that are referenced from the main application. I'm sure there is a reason for this process, however, I am unable to ascertain it at this time. I have come up with a few solutions for you:
- 使用脚本检测环境并相应地设置类路径。这可能是最简单的解决方案,但根据您的特定要求,您可能愿意也可能不想采用。
- 与其他答案中提到的类似,专门使用自定义类加载器加载和执行您的应用程序。这并不意味着创建一个将被加载然后调用您的应用程序的单个类。这意味着任何需要与动态加载的类交互
swt
需要引用应用程序类的库和任何类都应从自定义类加载器加载。任何应用程序依赖项,例如 log4j 等,都可以由默认应用程序 ClassLoader 引用。以下是其工作原理的示例:
JAR 1(启动器.jar):
public class AppLauncher {
public static void main(String… args) throws Exception {
ClassLoader loader = initClassLoader();
Class<?> mpClass = loader.loadClass("mp.MyProgram");
// using Runnable as an example of how it can be done
Runnable mpClass = (Runnable) mpClass.newInstance();
}
public static ClassLoader initClassLoader() {
// assuming still configured as system classloader, could also be initialized directly
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
// add the main application jar to the classpath.
// You may want to dynamically determine this value (lib folder) or pass it in as a parameter
loader.addJarToClasspath("myapp.jar");
String architecture = System.getProperty("os.arch");
try {
if (architecture.contains("64")) {
loader.addJarToClasspath("swt-3.6.1-win32-win32-x86_64.jar");
} else {
loader.addJarToClasspath("swt-3.6.1-win32-win32-x86.jar");
}
Class.forName("org.eclipse.swt.graphics.Point", false, loader);
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
} catch (Exception exception) {
exception.printStackTrace();
System.out.println("Could not load SWT library");
System.exit(1);
}
return loader;
}
JAR 2(myapp.jar):包括依赖于的所有类swt
public class MyProgram implements Runnable {
//…
public void run() {
// perform application execution
// this logic should now work
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0,0);
}
}
The AppLauncher
类将由 VM 执行,而应用程序的其余部分不会包含在执行 Jar 中。
java -Djava.system.class.loader=test.LibraryLoader -cp :launcher.jar mp.AppLauncher
我看到其他答案已经更新了。既然我已经把上面的评论打出来了,我想我还是应该把它贴出来供大家阅读。