Sto provando a creare un sistema di plugin per la mia applicazione e voglio iniziare con qualcosa di semplice. Ogni plug-in deve essere imballata in un file .jar e implementare l'interfaccia SimplePlugin
:Caricamento dinamico dei jar del plugin con ServiceLoader
package plugintest;
public interface SimplePlugin {
public String getName();
}
Ora ho creato un'implementazione di SimplePlugin
, confezionato in un .jar e metterlo nel plugin/sottodirectory dell'applicazione principale :
package plugintest;
public class PluginTest implements SimplePlugin {
public String getName() {
return "I'm the plugin!";
}
}
nel ricorso principale, voglio ottenere un'istanza di PluginTest
. Ho provato due alternative, entrambi con java.util.ServiceLoader
.
1. dinamicamente estende il percorso di classe
Questo utilizza l'hack conosciuti per usare riflessione sul caricatore classe sistema per evitare incapsulamento, al fine di aggiungere URL
s il classpath. viene aggiunto
package plugintest.system;
import plugintest.SimplePlugin;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ServiceLoader;
public class ManagePlugins {
public static void main(String[] args) throws IOException {
File loc = new File("plugins");
extendClasspath(loc);
ServiceLoader<SimplePlugin> sl = ServiceLoader.load(SimplePlugin.class);
Iterator<SimplePlugin> apit = sl.iterator();
while (apit.hasNext())
System.out.println(apit.next().getName());
}
private static void extendClasspath(File dir) throws IOException {
URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
URL urls[] = sysLoader.getURLs(), udir = dir.toURI().toURL();
String udirs = udir.toString();
for (int i = 0; i < urls.length; i++)
if (urls[i].toString().equalsIgnoreCase(udirs)) return;
Class<URLClassLoader> sysClass = URLClassLoader.class;
try {
Method method = sysClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(sysLoader, new Object[] {udir});
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Il/directory plugins come previsto (come si può controllare chiamando sysLoader.getURLs()
), ma poi l'iteratore in oggetto ServiceLoader
è vuoto.
2. Uso URLClassLoader
Questo utilizza un'altra definizione di ServiceLoader.load
con un secondo argomento della classe ClassLoader
.
package plugintest.system;
import plugintest.SimplePlugin;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ServiceLoader;
public class ManagePlugins {
public static void main(String[] args) throws IOException {
File loc = new File("plugins");
File[] flist = loc.listFiles(new FileFilter() {
public boolean accept(File file) {return file.getPath().toLowerCase().endsWith(".jar");}
});
URL[] urls = new URL[flist.length];
for (int i = 0; i < flist.length; i++)
urls[i] = flist[i].toURI().toURL();
URLClassLoader ucl = new URLClassLoader(urls);
ServiceLoader<SimplePlugin> sl = ServiceLoader.load(SimplePlugin.class, ucl);
Iterator<SimplePlugin> apit = sl.iterator();
while (apit.hasNext())
System.out.println(apit.next().getName());
}
}
Ancora una volta, l'iteratore non ha mai un elemento "successivo".
C'è sicuramente qualcosa che mi manca poiché è la prima volta che sto "giocando" con percorsi di classe e caricamento.
perché non utilizzare una semplice libreria con il supporto di riflessione? come la configurazione comune di apache con il supporto per i bean: http://commons.apache.org/proper/commons-configuration/userguide/howto_beans.html#Declaring_and_Creating_Beans –
@omerschleifer Perché, come ho detto, è la prima volta che gioco con questo genere di cose e voglio imparare come funzionano. In secondo luogo, il tuo consiglio è il benvenuto, ma le biblioteche hanno il problema comune che possono fare molto più di quello che vuoi fare, quindi le cose sono spesso più complicate del necessario. Non so se è il caso, ma voglio risolverlo in una libreria esterna solo se è la mia ultima possibilità. – MaxArt
Se questo è per scopi didattici, divertiti. ma per quanto riguarda la complessità, la riflessione è molto più facile da eseguire e padroneggiare utilizzando una libreria semplice come quella che ti ho inviato piuttosto che inventare la ruota da solo. La riflessione è, dopo tutto, un'area ben esplorata. buona fortuna –