Java Language
Javaプラグインシステムの実装
サーチ…
備考
IDEやビルドシステムを使用している場合は、この種のプロジェクトを設定する方がはるかに簡単です。メインのアプリケーションモジュールを作成してからAPIモジュールを作成し、プラグインモジュールを作成して、APIモジュールまたはその両方に依存させます。次に、プロジェクトの成果物をどこに配置するかを設定します。コンパイルされたプラグインのjarファイルを 'plugins'ディレクトリにまっすぐ送ることができ、手動での移動を避けることができます。
URLClassLoaderの使用
Javaアプリケーション用のプラグインシステムを実装する方法はいくつかあります。 URLClassLoaderを使用するのが最も簡単な方法の1つです。次の例では、JavaFXコードを少し使用します。
主なアプリケーションのモジュールがあるとします。このモジュールは 'plugins'フォルダからJarsの形式でプラグインを読み込むことになっています。初期コード:
package main;
public class MainApplication extends Application
{
@Override
public void start(Stage primaryStage) throws Exception
{
File pluginDirectory=new File("plugins"); //arbitrary directory
if(!pluginDirectory.exists())pluginDirectory.mkdir();
VBox loadedPlugins=new VBox(6); //a container to show the visual info later
Rectangle2D screenbounds=Screen.getPrimary().getVisualBounds();
Scene scene=new Scene(loadedPlugins,screenbounds.getWidth()/2,screenbounds.getHeight()/2);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] a)
{
launch(a);
}
}
次に、プラグインを表すインターフェースを作成します。
package main;
public interface Plugin
{
default void initialize()
{
System.out.println("Initialized "+this.getClass().getName());
}
default String name(){return getClass().getSimpleName();}
}
このインタフェースを実装するクラスをロードしたいので、最初に '.jar'拡張子を持つファイルをフィルタリングする必要があります:
File[] files=pluginDirectory.listFiles((dir, name) -> name.endsWith(".jar"));
ファイルがある場合は、URLとクラス名のコレクションを作成する必要があります。
if(files!=null && files.length>0)
{
ArrayList<String> classes=new ArrayList<>();
ArrayList<URL> urls=new ArrayList<>(files.length);
for(File file:files)
{
JarFile jar=new JarFile(file);
jar.stream().forEach(jarEntry -> {
if(jarEntry.getName().endsWith(".class"))
{
classes.add(jarEntry.getName());
}
});
URL url=file.toURI().toURL();
urls.add(url);
}
}
ロードされたプラグインを保持するMainApplicationに静的なHashSetを追加しましょう:
static HashSet<Plugin> plugins=new HashSet<>();
次に、 URLClassLoaderをインスタンス化し、クラス名を反復して、 プラグインインタフェースを実装するクラスをインスタンス化します。
URLClassLoader urlClassLoader=new URLClassLoader(urls.toArray(new URL[urls.size()]));
classes.forEach(className->{
try
{
Class cls=urlClassLoader.loadClass(className.replaceAll("/",".").replace(".class","")); //transforming to binary name
Class[] interfaces=cls.getInterfaces();
for(Class intface:interfaces)
{
if(intface.equals(Plugin.class)) //checking presence of Plugin interface
{
Plugin plugin=(Plugin) cls.newInstance(); //instantiating the Plugin
plugins.add(plugin);
break;
}
}
}
catch (Exception e){e.printStackTrace();}
});
次に、プラグインのメソッドを呼び出すことで、プラグインのメソッドを初期化することができます。
if(!plugins.isEmpty())loadedPlugins.getChildren().add(new Label("Loaded plugins:"));
plugins.forEach(plugin -> {
plugin.initialize();
loadedPlugins.getChildren().add(new Label(plugin.name()));
});
MainApplicationの最終コード:
package main;
public class MainApplication extends Application
{
static HashSet<Plugin> plugins=new HashSet<>();
@Override
public void start(Stage primaryStage) throws Exception
{
File pluginDirectory=new File("plugins");
if(!pluginDirectory.exists())pluginDirectory.mkdir();
File[] files=pluginDirectory.listFiles((dir, name) -> name.endsWith(".jar"));
VBox loadedPlugins=new VBox(6);
loadedPlugins.setAlignment(Pos.CENTER);
if(files!=null && files.length>0)
{
ArrayList<String> classes=new ArrayList<>();
ArrayList<URL> urls=new ArrayList<>(files.length);
for(File file:files)
{
JarFile jar=new JarFile(file);
jar.stream().forEach(jarEntry -> {
if(jarEntry.getName().endsWith(".class"))
{
classes.add(jarEntry.getName());
}
});
URL url=file.toURI().toURL();
urls.add(url);
}
URLClassLoader urlClassLoader=new URLClassLoader(urls.toArray(new URL[urls.size()]));
classes.forEach(className->{
try
{
Class cls=urlClassLoader.loadClass(className.replaceAll("/",".").replace(".class",""));
Class[] interfaces=cls.getInterfaces();
for(Class intface:interfaces)
{
if(intface.equals(Plugin.class))
{
Plugin plugin=(Plugin) cls.newInstance();
plugins.add(plugin);
break;
}
}
}
catch (Exception e){e.printStackTrace();}
});
if(!plugins.isEmpty())loadedPlugins.getChildren().add(new Label("Loaded plugins:"));
plugins.forEach(plugin -> {
plugin.initialize();
loadedPlugins.getChildren().add(new Label(plugin.name()));
});
}
Rectangle2D screenbounds=Screen.getPrimary().getVisualBounds();
Scene scene=new Scene(loadedPlugins,screenbounds.getWidth()/2,screenbounds.getHeight()/2);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] a)
{
launch(a);
}
}
2つのプラグインを作成しましょう。明らかに、プラグインのソースは別のモジュールになければなりません。
package plugins;
import main.Plugin;
public class FirstPlugin implements Plugin
{
//this plugin has default behaviour
}
2番目のプラグイン:
package plugins;
import main.Plugin;
public class AnotherPlugin implements Plugin
{
@Override
public void initialize() //overrided to show user's home directory
{
System.out.println("User home directory: "+System.getProperty("user.home"));
}
}
これらのプラグインは、標準のJarファイルにパッケージ化する必要があります。このプロセスは、IDEや他のツールによって異なります。
Jarsが 'プラグイン'に直接挿入されると、 MainApplicationはそれらを検出して適切なクラスをインスタンス化します。