自定义 URLClassLoader,运行时出现 NoClassDefFoundError
我创建了自己的 URLClassLoader
,并通过 java.system.class.loader
将其设置为系统类加载器。它已初始化且一切正常,但找不到我尝试加载的类。这是 URLClassLoader
:
public class LibraryLoader extends URLClassLoader
{
public LibraryLoader(ClassLoader classLoader)
{
super(new URL[0], classLoader);
}
synchronized public void addJarToClasspath(String jarName) throws MalformedURLException, ClassNotFoundException
{
File filePath = new File(jarName);
URI uriPath = filePath.toURI();
URL urlPath = uriPath.toURL();
System.out.println(filePath.exists());
System.out.println(urlPath.getFile());
addURL(urlPath);
}
}
我已确认该 jar 存在,并且路径正确。这就是我在程序中的调用方式:
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
loader.addJarToClasspath("swt.jar");
这是我得到的异常(第 166 行指的是我尝试创建新的 Point
的行:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/graphics/Point
at mp.MyProgram.loadArchitectureLibraries(MyProgram.java:116)
at mp.MyProgram.main(MyProgram.java:90)
Caused by: java.lang.ClassNotFoundException: org.eclipse.swt.graphics.Point
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 2 more
我什至尝试像这样显式加载该类:
Class.forName("org.eclipse.swt.graphics.Point", false, loader);
这可能是什么原因造成的?
更新:这是来自MyProgram
的重要代码
public class MyProgram
{
// ...
public static void main(String[] args)
{
loadArchitectureLibraries();
// ...
}
public static void loadArchitectureLibraries()
{
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
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);
}
}
}
更新2:这是一个SSCCE:http://nucleussystems.com/files/myprogram.zip 调用java。 -Djava.system.class.loader=mp.LibraryLoader -jar myprogram.jar
。
I've created my own URLClassLoader
, and set it as the system classloader via java.system.class.loader
. It's initialized and everything, but the classes I'm trying to load aren't found. Here's the URLClassLoader
:
public class LibraryLoader extends URLClassLoader
{
public LibraryLoader(ClassLoader classLoader)
{
super(new URL[0], classLoader);
}
synchronized public void addJarToClasspath(String jarName) throws MalformedURLException, ClassNotFoundException
{
File filePath = new File(jarName);
URI uriPath = filePath.toURI();
URL urlPath = uriPath.toURL();
System.out.println(filePath.exists());
System.out.println(urlPath.getFile());
addURL(urlPath);
}
}
I've confirmed that the jar exists, and that the path is correct. This is how I call it in my program:
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
loader.addJarToClasspath("swt.jar");
This is the exception that I get (line 166 refers to the line at which I try to create a new Point
:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/graphics/Point
at mp.MyProgram.loadArchitectureLibraries(MyProgram.java:116)
at mp.MyProgram.main(MyProgram.java:90)
Caused by: java.lang.ClassNotFoundException: org.eclipse.swt.graphics.Point
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 2 more
I even tried explicitly loading the class like so:
Class.forName("org.eclipse.swt.graphics.Point", false, loader);
What might be causing this? Shouldn't it "just work"?
Update: Here's the important code from MyProgram
public class MyProgram
{
// ...
public static void main(String[] args)
{
loadArchitectureLibraries();
// ...
}
public static void loadArchitectureLibraries()
{
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
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);
}
}
}
Update 2: Here's an SSCCE: http://nucleussystems.com/files/myprogram.zip . Call java -Djava.system.class.loader=mp.LibraryLoader -jar myprogram.jar
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我必须同意关于这个问题的评论。根据您提供的代码,您收到的错误似乎是由于 JAR 文件不在您期望的位置。正如@Andrew 所提到的,您没有在 addJarToClasspath 方法中检查文件是否存在。因此,如果该文件不存在,您将收到您所看到的 ClassNotFound 异常。我通过获取您的 ClassLoader 逻辑并向其传递有效和无效的 JAR 来验证此问题。当提供有效的 JAR/路径时,类加载器将按预期加载该类。当指定了无效的 JAR/路径时,我收到了您提到的错误。如果指定的 URL 不指向有效文件,则 URLClassLoader 不会引发异常。
要验证该场景,请打印出文件的完整路径,并查看它对于执行环境是否正确。
编辑
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
库交互的类以及任何需要引用您的应用程序类的类都应该从您的自定义 ClassLoader 加载。任何应用程序依赖项,例如 log4j 等,都可以由默认应用程序 ClassLoader 引用。以下是其工作原理的示例:JAR 1 (launcher.jar):
JAR 2 (myapp.jar): 包括依赖于
swt 的所有类
AppLauncher 类将由 VM 执行,而应用程序的其余部分不会包含在执行 Jar 中。
我发现其他答案已更新。既然我已经把上面的评论打出来了,我想我还是应该把它贴出来供大家阅读。
I would have to agree with the comments on this question. Based on the code you have provided, it would appear that you are getting the error due to the JAR files not being where you expect them to be. As mentioned by @Andrew, you are not checking the existence of the file in your addJarToClasspath method. As a result, if the file does not exist, you will receive a ClassNotFound exception as you are seeing. I verified this problem by taking your ClassLoader logic and passing it a valid and an invalid JAR. When a valid JAR/path was provided, the ClassLoader loaded the class as expected. When an invalid JAR/path was specified, I received the error you mentioned. The URLClassLoader does not throw an exception if an URL is specified that does not point to a valid file.
To validate the scenario, print out the path of the full path of your File and see if it is correct for the execution environment.
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
libraries and any classes that need to reference your application classes should be loaded from your custom ClassLoader. Any application dependencies, such as log4j, etc, can be referenced by the default application ClassLoader. Here is an example of how this would work:JAR 1 (launcher.jar):
JAR 2 (myapp.jar): Includes all class which depend on
swt
The
AppLauncher
class would be executed by the VM without the rest of your application being included in the execution Jar.I see that there have been updates to the other answers. Since I already had typed up the above comments, I figured that I should still post it for your perusal.
从几英里之外就可以看到,您没有在
Class.forName
旁边使用自定义类加载器。自从加载当前类
MyProgram
的类加载器尝试加载以来,就会发生 ClassNoDefFoundError加载 org.eclipse.swt.graphics.Point。您需要通过 Class.forName 加载另一个类(称为启动器),然后从那里开始 - 实现一些接口(甚至 runnable 也可以)并调用它。
编辑
如何做到这一点,一个简单的场景。
1. 创建另一个名为 mp.loader.Launcher 的类,该类实现 Runnable。
2. 将其放入另一个名为 swt-loader.jar 的 jar 中。
在 MyProgram 类中使用:
It's visible from a (few) mile(s) away you are not using the custom classloader beside
Class.forName
The ClassNoDefFoundError occurs since the classloader that has loaded current class
MyProgram
attempts to load org.eclipse.swt.graphics.Point.You need to load another class (call it launcher) via Class.forName and then start from there - implement some interface (even runnable will do) and call it.
edit
How to do it, a simplistic scenario.
1. Create another class called
mp.loader.Launcher
that implements Runnable like that.2. Place it in another jar called swt-loader.jar.
in MyProgram class use:
由于有问题的行不是 Class.forName 而是 Point 实例的实际初始化,因此我们必须确保尝试加载 Point 类的类是由 Library 类加载器创建的。因此,我根据 此博客对 LibraryLoader 做了一些细微的更改 在程序
本身中,我们必须提取一个新方法,因为从方法中使用的所有类似乎都是预先加载的:
这应该适合您。
Since the offending line is not the Class.forName but the actual initialization of an instance of Point, we'll have to make sure that the class, that tries to load the Point class, was created by the Library class loader. Therefore, I made some minor changes in the LibraryLoader accordingt to this blog entry
In the program itself, we have to extract a new method since all the classes, that are used from within a method, seem to be loaded up-front:
That should work for you.