Unsere Java-Anwendung ist auf einige Ressourcen angewiesen, die auf einer Netzwerkfreigabe verfügbar sind. Diese Netzwerkfreigabe befindet sich auf dem Klassenpfad, und die Ressourcen werden zur Laufzeit mit MyClass.class.getResourceAsStream("/myfile.jpg")
.
java -Djava.class.path=\\myserver\myshare:C:\myjar.jar MainClass
Wenn die Freigabe beim Starten verfügbar ist, läuft alles reibungslos. Bild- und Eigenschaftsdateien, die sich in der Freigabe befinden, können gelesen werden mit getResourceAsStream()
. Wenn die Aktie jedoch nicht online ist, wenn die Anwendung gestartet wird, auch wenn die Aktie online geht, bevor irgendwelche Ressourcen gelesen werden können sie nicht gelesen werden mit getResourceAsStream()
.
Bei meinen Recherchen mit eclispse + decompiler habe ich einen Unterschied festgestellt. Der Standard-Classloader erbt von URLClassLoader, und seine ucp
Mitglied ( URLClassPath
) enthält eine Liste von URLClassPath.Loader
Instanzen. Im ersten Szenario enthält sie eine URLClassPath.FileLoader
und eine URLClassPath.JarLoader
. Im zweiten Szenario enthält es nur einen Jar-Loader.
Es ist, als ob Java feststellt, dass der Klassenpfadeintrag ungültig ist und ihn komplett verwirft.
Warum ist das so? Wie kann ich es vermeiden?
Update Ich kann den Mechanismus, mit dem wir die Ressourcen laden, aus mehreren Gründen nicht ändern:
- Es gibt viel zu viele Bereiche, in denen Dateien auf diese Weise geladen werden, als dass ich derzeit etwas ändern könnte
- Es gibt Situationen, in denen die Ressource tatsächlich von einer Komponente eines Dritten geladen wird
Ich habe kein Problem damit, einen benutzerdefinierten Klassenlader zu erstellen, ich brauche nur eine Anleitung, wie ich es machen soll.
Ich habe es damit versucht, konnte aber nicht die erwarteten Ergebnisse erzielen:
import java.net.URL;
import java.net.URLClassLoader;
public class MyUrlClassLoader extends URLClassLoader {
public MyUrlClassLoader(ClassLoader parent) {
super(new URL[0], parent);
System.out.println("MyUrlClassLoader ctor");
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("url find class " + name);
return super.findClass(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("url load class " + name);
return super.loadClass(name);
}
@Override
public URL getResource(String name) {
System.out.println("url get resource " + name);
return super.getResource(name);
}
}
import java.net.URL;
public class ClassLoaderMain {
public static void main(String[] args) throws ClassNotFoundException {
URL url = ClassLoaderMain.class.getResource("/myfile.txt");
System.out.print("Loaded? ");
System.out.println(url != null);
System.out.println(ClassLoaderMain.class.getClassLoader().toString());
System.out.println(MyUrlClassLoader.class.getClassLoader().toString());
System.out.println(FakeClass.class.getClassLoader().toString());
}
}
Wenn ich laufe java -cp . -Djava.system.class.loader=MyUrlClassLoader ClassLoaderMain
Diese Ausgaben:
MyUrlClassLoader ctor
url load class java.lang.System
url load class java.nio.charset.Charset
url load class java.lang.String
url load class ClassLoaderMain
Loaded? true
sun.misc.Launcher$AppClassLoader@923e30
sun.misc.Launcher$AppClassLoader@923e30
sun.misc.Launcher$AppClassLoader@923e30
So meine Klasse Loader erstellt wird, und Klasse laden wird aufgerufen, aber es scheint nicht zu sein, die Klasse Loader für die Klassen, die es lädt?