Dynamiczne ładowanie słoików wtyczek za pomocą ServiceLoadera

Próbuję stworzyć system wtyczek dla mojej aplikacji i chcę zacząć od czegoś prostego. Każda wtyczka powinna być zapakowana w plik .jar i zaimplementowaćSimplePlugin berło:

package plugintest;

public interface SimplePlugin {
    public String getName();
}

Teraz stworzyłem implementacjęSimplePlugin, zapakowane w .jar i umieść je w podkatalogu plugin / głównej aplikacji:

package plugintest;

public class PluginTest implements SimplePlugin {
    public String getName() {
        return "I'm the plugin!";
    }
}

W głównej aplikacji chcę uzyskać wystąpieniePluginTest. Wypróbowałem dwie alternatywy, obie używającejava.util.ServiceLoader.

1. Dynamiczne rozszerzanie ścieżki klasy

Używa znanego hacku, aby użyć refleksji nad programem ładującym klasy systemu, aby uniknąć enkapsulacji, aby dodaćURLs Ścieżka klasy.

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();
        }
    }
}

Katalog wtyczek / katalogów jest dodawany zgodnie z oczekiwaniami (ponieważ można sprawdzić wywołanie)sysLoader.getURLs()), ale potem iterator podany przezServiceLoader obiekt jest pusty.

2. Korzystanie z URLClassLoader

To używa innej definicjiServiceLoader.load z drugim argumentem klasyClassLoader.

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());
    }
}

Po raz kolejny iterator nigdy nie ma elementu „następnego”.

Z pewnością brakuje mi czegoś, ponieważ po raz pierwszy „bawię się” ścieżkami klas i ładowaniem.

questionAnswers(3)

yourAnswerToTheQuestion