|
Durant le développement d'une application modulaire, il peut être utile de proposer à d'autres développeurs l'élaborations de modules additionels (plug-in, fonctionnalités, ...). De manière classique, cela se passe de la manière suivante :
- Vous développez votre application normalement
- Vous élaborez une interface exploitable par d'autres développeurs qui contient des setters permettant de fournir au plug-in de quoi intéragir avec l'application ; cette interface définit également les intéraction fonctionnelle de l'applicatoin avec le plug-in.
- Tout plug-in doit implémenter cette interface pour être en mesure d'intégrer l'application.
Avec l'injection de dépendance, vous pouvez vous affranchir de ces setters qui n'ont aucune utilité fonctionnelle autre qu'à l'initialisation du plug-in:
- Vous développez votre application normalement.
- Vous élaborez une interface définissant les interactions fonctionnelles avec un plug-in. Cette interface n'a pas à implémenter de setters d'initialisation.
- Vous élaborez des annotations (Java 5) qui serviront de "marqueurs" au sein des plug-in, pour leur injecter les éléments qu'ils désirent.
- Tout plug-in doit implémenter l'interface fonctionnelle qui définit un plug-in (ce qui est classique) mais peut maintenant annoter des variables d'instances à l'aide d'annotations afin de récupérer des dépendances.
Voici un exemple d'utilisation :
// Annotation fournie par l'application principale (développeur A) package org.test.app.extension; @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) // pour rendre disponible l'annotation au moment de l'exécution de l'application @java.lang.annotation.Target( { java.lang.annotation.ElementType.FIELD } ) // pour que l'annotation ne puisse être utilisée qu'avec des variables d'instance public @interface Engine { }
package org.test.app.extension; public interface Plugin { public void execute(); }
// Plug-in (développeur X) package foo.bar; import org.test.app.component.MainComponent; import org.test.app.extension.Engine; import org.test.app.extension.Plugin; public class MyPlugin implements Plugin { @Engine private MainComponent main; // main va pouvoir être automatiquement affecté par l'applicatoin qui hébergera le plugin (même avec private !)
public void execute() { main.someMethod("foo", "bar"); // n'importe quelle méthode définie dans MainComponent } }
Le code qui permet de mettre en oeuvre cette injection de dépendance est le suivant (maintenu par le développeur de l'application principale) :
// importations: java.lang.reflect.* java.util.* for (Field field : somePluginInstance.getClass().getDeclaredFields()) { log.debug("Scanning for injection: " + field); // système de log, à adapter suivant la situation - FACULTATIF if (field.isAnnotationPresent(org.test.app.extension.Engine.class) && field.getType().isAssignableFrom(org.test.app.component.MainComponent.class)) { try { field.setAccessible(true); // permet d'affecter les champs "private" (si vous avec un SecurityManager, celui-ci est à adapter pour autoriser ce type d'accès) log.debug("Injecting " + this.chat); // FACULTATIF field.set(somePluginInstance, mainComponent); // injection de "mainComponent" } catch (IllegalArgumentException e) { log.error(e); // FACULTATIF } catch (IllegalAccessException e) { log.error(e); // FACULTATIF } }
|