Wenn man größere Applikationen entwickelt kommt man irgendwann einmal an den Punkt, wo verschiedene Libraries, die man gerne verwenden möchte, ihrerseits von anderen Libraries abhängen. Da kommt dann auch schonmal dieselbe Library mehrfach vor, nur leider mit unterschiedlichen Versionen. Man probiert dann immer gerne, ob es nicht eine Version gibt, mit der beide funktionieren und manchmal hat man Glück, bis dann eine dritte kommt. Im Prinzip ist es möglich, auch so etwas zu lösen, aber es ist schon konzeptionell nicht so einfach und in der Praxis auch etwas mühsam, wenn es überhaupt gelingt. Zum Glück ist es in vielen Fällen möglich, einfach die neueste der verlangten Versionen zu nehmen, aber manchmal geht das nicht, sei es, weil die Version wirklich überprüft wird und eine bestimmte verlangt wird oder sei es, weil diese beiden Versionen wirklich an einer Stelle inkompatibel sind, die dummerweise noch verwendet wird.
In Java gibt es dafür einen Ansatz, eigene ClassLoader zu verwenden. Hier ein kleines Beispiel, in dem eine Klasse X drei Objekte aus verschiedene Klassen Y instanziiert. Diese Klassen Y sind alle im selben Package, aber in verschiedenen Jars.
1. X.java
import java.net.URL; import java.net.URLClassLoader; public class X { public static void main(String args[]) { try { ClassLoader c0 = Thread.currentThread().getContextClassLoader(); URL cpa = new URL("file:///home/bk1/src/class-loader/A/A.jar"); URL cpb = new URL("file:///home/bk1/src/class-loader/B/B.jar"); URL cpc = new URL("file:///home/bk1/src/class-loader/C/C.jar"); ClassLoader ca = new URLClassLoader(new URL[]{ cpa }, c0); ClassLoader cb = new URLClassLoader(new URL[]{ cpb }, c0); ClassLoader cc = new URLClassLoader(new URL[]{ cpc }, c0); Object a = ca.loadClass("Y").newInstance(); System.out.println("a=" + a + " a.class=" + a.getClass()); Object b = cb.loadClass("Y").newInstance(); System.out.println("b=" + b + " b.class=" + b.getClass()); Object c = cc.loadClass("Y").newInstance(); System.out.println("c=" + c + " c.class=" + c.getClass()); } catch (Exception ex) { throw new RuntimeException(ex); } } }
2. A/Y.java
public class Y { public String toString() { return "Feuer"; } }
Dies landed kompiliert in A.jar
3. B/Y.java
public class Y { public String toString() { return "Wasser"; } }
Dies landed kompiliert in B.jar
4. C/Y.java
public class Y { public String toString() { return "Erde"; } }
Dies landed kompiliert in C.jar
Und das ist die Ausgabe:
a=Feuer a.class=class Y b=Wasser b.class=class Y c=Erde c.class=class Y
Natürlich sollte man dies bevörzugt Frameworks überlassen, statt es selber zu programmieren. Aber grundsätzlich ist das Problem durchaus lösbar.