Java Language
Implementaciones del sistema de plugin Java
Buscar..
Observaciones
Si usa un IDE y / o un sistema de compilación, es mucho más fácil configurar este tipo de proyecto. Crea un módulo de aplicación principal, luego el módulo API, luego crea un módulo de complemento y lo hace dependiente del módulo API o de ambos. A continuación, configure dónde se colocarán los artefactos del proyecto; en nuestro caso, los archivos compilados de complementos se pueden enviar directamente al directorio de "complementos", evitando así realizar movimientos manuales.
Utilizando URLClassLoader
Hay varias formas de implementar un sistema de complementos para una aplicación Java. Una de las más sencillas es usar URLClassLoader . El siguiente ejemplo involucrará un poco de código JavaFX.
Supongamos que tenemos un módulo de una aplicación principal. Se supone que este módulo carga complementos en forma de archivos jar de la carpeta 'complementos'. Código inicial:
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);
}
}
Luego, creamos una interfaz que representará un complemento.
package main;
public interface Plugin
{
default void initialize()
{
System.out.println("Initialized "+this.getClass().getName());
}
default String name(){return getClass().getSimpleName();}
}
Queremos cargar clases que implementen esta interfaz, así que primero necesitamos filtrar los archivos que tienen una extensión '.jar':
File[] files=pluginDirectory.listFiles((dir, name) -> name.endsWith(".jar"));
Si hay algún archivo, necesitamos crear colecciones de URL y nombres de clase:
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);
}
}
Agreguemos un HashSet estático a MainApplication que contendrá los complementos cargados:
static HashSet<Plugin> plugins=new HashSet<>();
A continuación, creamos una instancia de URLClassLoader e iteramos sobre los nombres de las clases, creando instancias que implementan la interfaz del complemento :
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();}
});
Luego, podemos llamar métodos de plugin, por ejemplo, para inicializarlos:
if(!plugins.isEmpty())loadedPlugins.getChildren().add(new Label("Loaded plugins:"));
plugins.forEach(plugin -> {
plugin.initialize();
loadedPlugins.getChildren().add(new Label(plugin.name()));
});
El código final de 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);
}
}
Vamos a crear dos complementos. Obviamente, la fuente del complemento debe estar en un módulo separado.
package plugins;
import main.Plugin;
public class FirstPlugin implements Plugin
{
//this plugin has default behaviour
}
Segundo complemento:
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"));
}
}
Estos complementos deben estar empaquetados en tarros estándar: este proceso depende de su IDE u otras herramientas.
Cuando Jars se coloque en 'plugins' directamente, MainApplication los detectará y creará las clases apropiadas.