Also bei mir klappt folgendes super: Ich habe eine Annotation geschrieben, welche den Namen Plugin trägt und eine Eigenschaft entryPoint besitzt. entryPoint besitzt dabei die Bezeichnung der Methode, welche als Plug-Startpunkt fungieren soll. Dann habe ich ein Plugin geschrieben, welches TestPlugin heißt und auf das ich die Annotation Plugin angewendet habe. TestPlugin besitzt natürlich eine kleine Implementierung, damit man auch sieht, dass es arbeitet. Ich habe TestPlugin in eine Jar exportiert. Diese Jar wird dann mittels URLClassLoader geladen, ein Objekt instanziert und der entryPoint aufgerufen. Den entryPoint bzw. die Methode, welche als entryPoint dient, habe ich mit einem Rückgabewert vom Typ Object und mit einem Object-Array für die Parameter ausgestattet. Der Rückgabetyp ist für die Signatur eigentlich egal, aber die Parameter müssen stimmen, daher wäre es besser, wenn man noch ein Interface deklarieren würde, das alle Plugins implementieren müssen, dann wäre aber der Methodennamen festgelegt und man könnte sich das mit der Annontation sparen. Wäre aber sicherer und nicht so Fehleranfällig.
So, hier meine Sources. Über Packagenamen nicht wundern, hat Ordnungsgründe und das Ladeverfahren der Plugins ist hardcoded, aber ich werde jetzt auch keine dynamisch Routine für ein Beispiel zusammenschustern, ist schließlich nur ein fiktives Beispiel.
Plugin.java
Java:
package de.tutorials.trilian.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Plugin {
String entryPoint() default "pluginMain";
}
TestPlugin.java
Java:
package de.tutorials.trilian.annotations;
@Plugin
public class TestPlugin {
public Object pluginMain(Object[] args) {
System.out.println("Testplugin gestartet");
System.out.println("Parameter:");
System.out.println("Anzahl: " + args.length);
for(int i = 0; i < args.length; i++) {
System.out.println(i + " => " + args[i].toString());
}
return null;
}
}
AnnontationLoadingSample.java
Java:
package de.tutorials.trilian.annotations;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class AnnotationLoadingSample {
public static void main(String[] args) throws MalformedURLException,
ClassNotFoundException, InstantiationException,
IllegalAccessException, SecurityException, NoSuchMethodException,
IllegalArgumentException, InvocationTargetException {
URLClassLoader loader = new URLClassLoader(new URL[]{new URL(
"file://localhost/home/link/plugins.jar")});
Class<?> pluginClass = loader.loadClass("de.tutorials.trilian.annotations.TestPlugin");
Plugin pluginAnnotation = pluginClass.getAnnotation(Plugin.class);
if(pluginAnnotation == null) { return; }
Object plugin = pluginClass.newInstance();
String entryPoint = pluginAnnotation.entryPoint();
Object[] paramArgs = new Object[]{"param1", "param2"};
Object[] methodParams = new Object[]{paramArgs};
pluginClass.getMethod(entryPoint, Object[].class).invoke(plugin,
methodParams);
}
}
Ausgabe:
Testplugin gestartet
Parameter:
Anzahl: 2
0 => param1
1 => param2
EDIT:
Habe mal noch ein Plugin gebastelt, das aber einen anderen entryPoint besitzt, um zu zeigen, dass man den entryPoint dynamisch für jedes Plugin festlegen kann und nicht den von Plugin festgelegten Standard-entryPoint verwenden muss.
TestPlugin2.java
Java:
package de.tutorials.trilian.annotations;
@Plugin(entryPoint = "meineMain")
public class TestPlugin2 {
public Object meineMain(Object[] args) {
System.out.println("Methode meineMain aus TestPlugin2 aufgerufen.");
return null;
}
}
Nun habe ich halt beide Plugin-Klassen in plugins.jar exportiert, damit beide vorhanden sind.
In AnnotationLoadingSample.java muss dann die Zeile
Class<?> pluginClass = loader.loadClass("de.tutorials.trilian.annotations.TestPlugin");
gegen
Class<?> pluginClass = loader.loadClass("de.tutorials.trilian.annotations.TestPlugin2");
geändert werden, damit die neue Plugin-Klasse geladen wird.
Ausgabe:
Methode meineMain aus TestPlugin2 aufgerufen.