2012-12-13 18 views
5

Ich möchte eine Anwendung, die Plug-Ins dynamisch laden kann, aber ich habe keine Literatur im Internet gefunden.Java dynamisch Plugin laden

Das Schwierige ist: Ich kenne den Namen nicht im Voraus.

Zum Beispiel habe ich eine Plugin Schnittstelle:

public interface Plugin { 
    public static Plugin newPlugin(); 
    public void executePlugin(String args[]); 
} 

Damit jede Klasse Plugin in der JAR-Datei Implementierung in einer Liste instanziiert werden:

Method method = classToLoad.getMethod ("newPlugin"); 
mylist.add(method.invoke(null); 
  1. Erste Problem ist, ich kann keine statische Methode in einer Schnittstelle haben.
  2. Zweites Problem ist, ich weiß nicht, wie alle Klassen zu finden, die eine Schnittstelle

Danke für Ihre Hilfe umzusetzen.

+0

Gibt es eine Konfigurationsdatei, die angibt, welche Plugins eine geeignete Lösung für Ihr Problem laden? – Mac

+0

Hallo Mac, in meinem Fall möchte ich vermeiden, eine Konfigurationsdatei zu haben, nur wenn ein Jar in einem Ordner vorhanden ist, versuchen Sie es zu laden. Vielen Dank. – FloFu

Antwort

20

So scheint es, als ob Sie dynamisch Klassen entdecken möchten, die eine bestimmte Schnittstelle (z. B. Plugin) zur Laufzeit implementieren. Sie haben grundsätzlich zwei Möglichkeiten dafür:

  1. Verwenden Sie ein Komponenten-Framework wie osgi
  2. Verwenden Java internen Ermittlungsprozess (ServiceLoader)

Da viele gute Tutorials auf OSGi (auch kleine) gibt es Das werde ich hier nicht näher ausführen. Um Java-internen Ermittlungsprozess zu verwenden, müssen Sie Folgendes tun:

  • Bundle alle „neuen“ Klassen, die Sie wünschen, zu entdecken in einer JAR-Datei
  • Erstellen Sie eine neue Datei innerhalb die JAR-Datei: META-INF/services/package.Plugin Sie müssen das volle Paket Qualifier hier verwenden
  • Diese Datei ist eine einfache Textdatei und enthält den vollständig qualifizierten Namen jeder Klasse Plugin in diesem jar-Datei
  • Ort, der jAR-Datei in das classpath Ihres (möglicherweise bereits die Umsetzung läuft) Anwendung
  • Entdecken Sie die Dienstleistungen:

    ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class) 
    for (Plugin p : loader) { 
        // do something with the plugin 
    } 
    

    Es gibt weitere Details hier: http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html

    Wie für statische Methoden in Schnittstellen: nicht möglich

Service-Entdeckung, wie das gemacht wird. Die Semantik wäre auch etwas seltsam, da auf statische Methoden ohne eine Instanz einer Klasse zugegriffen werden kann und Interfaces nur die Methoden ohne jede Funktionalität definieren. So würde statische erlauben, Interface.doSomething() zu rufen, während die Schnittstelle keine Funktionalität definiert, führt dies nur zu Verwirrung.

edit:

hinzugefügt Beschreibung, was in der Meta-Datei

+0

Ich glaube nicht, dass jemand gesagt hat, Sie willkommen zu heißen, SirRichie, aber Sie sind herzlich willkommen in dieser Gemeinschaft. Stimmen Sie alle vier Ihrer Antworten :) –

+0

Vielen Dank :) Jetzt muss ich nur lernen, wie man richtig Code formatieren ... – SirRichie

+0

Normalerweise einrücken Sie nur die erste Zeile mit 4 Leerzeichen, nachdem Sie eine Zeile übersprungen. Nach einer weiteren Leerzeile beginnen Sie an der Position 0 für den Rest Ihres normalen Textes. Sie können Code mit '' Zeichen für Inline-Code umgeben. –

2

In Bezug auf Ihrem erstes Problem, nicht in der Lage zu sein, um statische Methoden in der Schnittstelle zu haben, ist mein Vorschlag verwenden sollte, um einfach die Schnittstelle a hat markieren und instanziieren.

Sie Plugin-Schnittstelle kann einfach sein:

public interface Plugin { 
    public void executePlugin(String args[]); 
} 

Und dann können Sie tun:

if (someClass instanceOf Plugin) { 
    mylist.add(someClass.newInstance()); 
} 

Dies führt zur zweiten Frage, wie werden Sie die someClass Referenz erhalten. Es gibt keinen Standard Weg zu finden alle Klassen implementieren eine bestimmte Schnittstelle in Ihrem Klassenpfad, obwohl ein Ansatz, den Sie tun können, scannt die Gläser in Ihrem Classpath, wenn eine gegebene Datei endet mit .class ermitteln Sie es voll qualifizierten Namen durch es ist Weg in das Glas und verwenden Sie Class.forName() Methode, um die Klasse zu materialisieren.

In Pseudo-Code so etwas wie diese:

for each jar in your classpath { 
    for each file in JarFile { 
    if (file ends with .class) { 
     materialize class using Class.forName 
    } 
    } 
} 

Mit der Class Instanz, die Sie überprüfen können, ob es Ihre Plugin Schnittstelle implementiert.

Denken Sie auch daran, dass Sie, wenn Sie Ihren Plugins einen Kontext hinzufügen müssen, in jedem Plugin, das Ihr Kontextobjekt empfängt, einen Konstruktor erstellen können, anstatt den Standardkonstruktor zu haben. In diesem Fall müssten Sie anstelle von newInstance() den Konstruktor mit den gewünschten Argumenten über die Reflektion abrufen.