Wie kann man eine Klasse aus einer *.jar-Datei auslesen und deren Methoden ausführen?v.5.0
Gehen wir von einer Klasse Test.java
aus, die kompiliert
und als Test.jar
gepackt wird. Denken wir weiterhin, dass Test.java
im package jar
liegt, sodass der komplette Pfad im
Klassenpfad jar/Test.java
lautet. Die
Klasse enthält einige Methoden die sich teilweise gegenseitig
aufrufen und lediglich Strings zur Dokumentation ausgeben.
public class Test {
public Test() {
System.out.println("Test aufgerufen");
init();
}
public void init() {
System.out.println("Test.init() aufgerufen");
}
public void init(String s, int i) {
System.out.println(s + i);
double d = (double) i;
gibAus(d);
init();
}
public double gibAus(double d) {
System.out.println("Test.gibAus() aufgerufen mit Parameter " + d);
return d;
}
public static void main(String[] args) {
new Test();
}
}
Die Klasse JarAuslesen ermittelt die gesuchte Klasse Test
aus der jar-Datei und führt deren Methoden aus.
In main()
werden zwei Methoden aufgerufen:
readClassFromJar()
gibt die in der *.jar ermittelte Klasse als class-Objekt zurück. Die Ausgabe wird auf der Konsole ausgegeben.loadMethods()
ruft die inTest.java
deklarierten Methoden auf.
Um die Klasse zu ermitteln wird in der Methode readClassFromJar()
der ClassLoader der aktuellen Klasse geladen. Er sucht die
gewünschte jar-Datei über ein URL-Objekt im ClassPath.
Die URL wird verwendet um ein Fileobjekt zu bilden, mit dem die
Instanz eines Jarfiles erzeugt wird.
Schließlich werden das
Manifest ausgelesen und über ein Attributs-Objekt der Name der
main-Klasse ermittelt. Im Manifest wird der Pfad der gesuchten Datei
mit '/' notiert. Die Slashes müssen gegen Punkte getauscht
werden um der Package-Syntax zu entsprechen. Nun kann die Klasse mit
Hilfe des ClassLoaders geladen werden. Das ermittelte Class-Objekt
kann schließlich von der Methode zurück gegeben werden.
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public class JarAuslesen {
@SuppressWarnings("unused")
private InputStream readStreamFromJar(String pkgname, String fname) {
String res = "/" + pkgname.replace('.', '/') + "/" + fname;
Class<?> clazz = readClassFromJar(pkgname, fname);
return clazz.getResourceAsStream(res);
}
private Class<?> readClassFromJar(String pkgname, String fname) {
Class<?> clazz = null;
ClassLoader cl = getClass().getClassLoader();
// hier wird die *.jar ermittelt
URL url = cl.getResource(pkgname + "/" + fname);
// Manifest ermitteln
File f = new File(url.getFile());
JarFile jf;
Manifest manifest = null;
try {
jf = new JarFile(f);
manifest = jf.getManifest();
} catch (IOException e) {
System.err.println("Ein-Ausgabe-Fehler bei Manifestermittlung.");
}
// Mainklasse ermitteln
String mc = null;
if (manifest != null) {
Attributes att = manifest.getMainAttributes();
mc = att.getValue(Attributes.Name.MAIN_CLASS);
}
try {
// hier muss der Packagename (mit '.') innerhalb der *.jar
// ohne Dateiendung angegeben werden
mc = mc.replace('/', '.');
clazz = cl.loadClass(mc);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return clazz;
}
private void loadMethods(String name) throws Exception {
Class<?> c;
c = Class.forName(name);
Object o = c.newInstance();
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
System.out.println("\nMethode: " + m[i].toString());
System.out.println("Methodenname: " + m[i].getName());
System.out.println("Return-Typ: " + m[i].getGenericReturnType());
try {
Type[] pType = m[i].getGenericParameterTypes();
for (Type type : pType) {
System.out.println("Parameter-Typ: " + type.toString());
if (type.equals(double.class))
m[i].invoke(o, Math.PI);
if (type.equals(int.class))
m[i].invoke(o, 4711);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
String path = "jar";
String file = "Test.jar";
JarAuslesen ja = new JarAuslesen();
System.out.println(ja.readClassFromJar(path, file));
ja.loadMethods(ja.readClassFromJar(path, file).getName());
}
}
Um die Methoden auszuführen, lädt die Methode loadMethods()
die ermittelte Klasse und erzeugt ein Objekt des ermittelten Typs.
Die Methode getDeclaredMethods()
der Klasse Class erzeugt
ein Method-Array, dessen Einträge in einer Schleife ausgelesen
werden. Ermittelt werden der Name, der Rückgabetyp und - in
einer weiteren Schleife - die Parametertypen. Sie werden jeweils in
einem Array abgelegt, dessen Länge abgefragt wird. Sind zwei
Parameter vorhanden, werden die ermittelten Parameter mit Werten
belegt und die hier zugehörige Methode init(String s, int
i)
ausgeführt.
Dies geschieht über Method.invoke()
.
Method.invoke()
kann eine variable Parameterzahl
entgegennehmen: Der erste repräsentiert das Objekt, auf dem die
Methode ausgeführt werden soll, die folgenden die Werte der
jeweiligen Parameter. Hier wird ein String und ein Integer
übergeben, wobei letzterer durch Autoboxing aus der Variablen int
zahl
gecastet wird. Auf diese Weise können auch primitive
Datentypen übergeben werden.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.