我已经创建了自己的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
。
答案 0 :(得分:2)
我必须同意对这个问题的评论。根据您提供的代码,由于JAR文件不在您期望的位置,您似乎会收到错误。正如@Andrew所提到的,您没有在addJarToClasspath方法中检查文件是否存在。因此,如果该文件不存在,您将看到一个ClassNotFound异常。我通过使用ClassLoader逻辑并将其传递给有效且无效的JAR来验证此问题。当提供有效的JAR /路径时,ClassLoader按预期加载了类。当指定了无效的JAR /路径时,我收到了您提到的错误。如果指定的URL未指向有效文件,则URLClassLoader不会引发异常。
要验证方案,请打印文件完整路径的路径,并查看它是否适用于执行环境。
修改强>
<小时/> 看来,即使您覆盖系统ClassLoader,VM仍将使用默认的sun.misc.Launcher$AppClassLoader
来加载某些类。在我的测试中,这包括从主应用程序引用的类。我确信这个过程是有原因的,但是,我现在无法确定它。我已经为您提出了一些解决方案:
swt
库交互的类以及需要引用应用程序类的任何类都应该从自定义的ClassLoader加载。默认应用程序ClassLoader可以引用任何应用程序依赖项,例如log4j等。以下是一个如何工作的示例:JAR 1(launcher.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);
}
}
AppLauncher
类将由VM执行,而其余的应用程序不会包含在执行Jar中。
java -Djava.system.class.loader = test.LibraryLoader -cp&lt; dependency jars&gt;:launcher.jar mp.AppLauncher
我看到其他答案都有更新。由于我已经输入了上述评论,我认为我仍然应该发布它以供您阅读。
答案 1 :(得分:1)
由于违规行不是Class.forName而是Point实例的实际初始化,我们必须确保试图加载Point类的类是由Library类加载器创建的。因此,我根据this blog entry
对LibraryLoader进行了一些小的更改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);
}
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if ("mp.MyProgram".equals(name)) {
return getClass(name);
}
return super.loadClass(name, resolve);
}
private Class<?> getClass(String name) throws ClassNotFoundException {
String file = name.replace('.', File.separatorChar) + ".class";
byte[] b = null;
try {
b = loadClassData(file);
Class<?> c = defineClass(name, b, 0, b.length);
resolveClass(c);
return c;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private byte[] loadClassData(String name) throws IOException {
InputStream stream = getClass().getClassLoader().getResourceAsStream(
name);
int size = stream.available();
byte buff[] = new byte[size];
DataInputStream in = new DataInputStream(stream);
in.readFully(buff);
in.close();
return buff;
}
}
在程序本身中,我们必须提取一个新方法,因为在方法中使用的所有类似乎都是预先加载的:
public class MyProgram {
public static void main(String[] args) {
LibraryLoader loader = (LibraryLoader) ClassLoader.getSystemClassLoader();
String architecture = System.getProperty("os.arch");
try {
loader.addJarToClasspath("swt.jar");
otherMethod();
} catch (Throwable exception) {
// println instead of logger because logging is useless at this level
exception.printStackTrace();
System.out.println("Could not load SWT library");
System.exit(1);
}
}
protected static void otherMethod() {
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
System.out.println("Works!");
}
}
那应该适合你。
答案 2 :(得分:1)
从(几英里)远的地方可以看到您没有使用Class.forName
旁边的自定义类加载器
发生ClassNoDefFoundError,因为已加载当前类MyProgram
的类加载器尝试加载org.eclipse.swt.graphics.Point。
你需要通过Class.forName加载另一个类(称之为启动程序)然后从那里开始 - 实现一些接口(甚至runnable会这样做)并调用它。
修改的
怎么做,一个简单的场景。
1.创建另一个名为mp.loader.Launcher
的类,它实现Runnable。
public class Launcher implements Runnable{
public void run(){
org.eclipse.swt.graphics.Point pt = new org.eclipse.swt.graphics.Point(0, 0);
//whatever, start from here.
}
}
2。将它放在另一个名为swt-loader.jar的jar中。
在MyProgram类中使用:
loader.addJarToClasspath("swt-loader.jar");
Runnable r = (Runnable) Class.forName("mp.loader.Launcher", true, loader);
r.run();//there you have