Injection de dépendance : Guice et Spring

Spring IoC

Spring

Spring est un framework libre de développement d'application java. Il s'agit d'un framework dit "léger" d'appplications car il permet au développeur de créer des serveurs d'application (JEE)  sans se soucier des couches basses. Néanmoins, le framework qui gère tout le système en dessous n'est pas léger d'un point de vue ressource.

Dans ce système qui facilité le travail du développeur, il y a beaucoups de dépendances et Spring a donc eu le besoin de développer son propre système de gestion de dépendances. Il s'agit du second gros point couvert par le framework de spring : spring IoC (Inversion of Control).

Spring est un framework beaucoup utilisé tant bien dans le monde libre que dans les entreprises. Il a été le premier framework "récent" d'injection de dépendances. Il possède une grande communauté qui le supporte (spring source) et il y a donc beaucoups de documentation, de manuels et d'aide sur les forums.

Fonctionnement

Spring IoC étant un framework très complet et complexe, il ne sera décrit en détail qu'une des multiples manières de faire de l'injection de dépendance avec Spring. Une petite explication des autres méthodes sera donnée a la fin de cet article.

Le fonctionnement de Springn IoC repose sur 3 éléments clés :
  • La configuration XML pour lier les implémentations à leurs interfaces
  • Les classes sont des beans et possèdent donc des getters et setters pour les champs à injecter
  • L'injection en même est effectuée par un ApplicationContext de Spring

  • Contexte de l'application

    Afin de pouvoir comparer correctement les deux solutions, nous reprendrons le même contexte que pour Google Guice.
    Pour notre exemple nous allons avoir une projet avec une classe principale qui va publier un lien sur un site web. Ce lien pouvant être compliqué nous souhaitons qu'il puisse être simplifié.
    Nous avons donc :

  • Main.java : classe appelée au lancement de l'application, cette dernière va simplement lancer notre classe principale TweetClient.java. Elle utilisera les implémentations BasicShortener et SmsTweeter
  • TweetClient.java : classe principale chargée de publier le lien sur internet (ici nous utiliserons la console). Elle utilisera 2 interfaces : Shortener (pour simplifier le lien) et Tweeter (pour publier le message)
  • BasicShortener.java et MockShortener.java sont 2 implémentations de Shortener
  • SmsTweeter.java et MockTweeter.java sont deux implémentations de Tweeter
  • Shortener.java et Tweeter.java sont les deux interfaces qui définissent les méthodes pour publier et simplifier un lien
  • Tests.java est une classe de test qui lancera notre application avec les implémentations MockShortener et MockTweeter

  • La configuration XML

    La configuration de spring se fait via des fichiers XML. Ces derniers sont utilisés dans tout système utilisant spring (pas seulement spring IoC). En cela, il est plus simple d'utiliser toute la suite Spring plutot qu'un élément isolé.
    Dans ce fichier XML toute classe est un bean et nous allons tout d'abord déclarer nos implémentations. Une fois les implémentations connues dans le fichier xml, nous pouvons déclarer les champs necessitant une injection et quelle implémentations ils doivent utiliser.
    Dans notre cas, nous aurons 2 fichiers xml, un pour le lancement normal et un pour les tests. Le fichier xml devra ressembler à ça :
    <bean id="BasicShortener" class="impl.BasicShortener"></bean>
    <bean id="SmsTweeter" class="impl.SmsTweeter"></bean>

    <bean id="tweetClient" class="core.TweetClient">
       <property name="tweet" ref="SmsTweeter"/>
       <property name="shorten" ref="BasicShortener"/>
    </bean>

    Nous avons déclaré 2 beans : BasicShortener (associé a la classe impl.BasicShortener) et SmsTweeter (associé a impl.smsTweeter). Nous avons ensuite déclaré une injection à faire dans core.TweetClient en associant l'attribut nommé tweet à l'implémentation SmsTweeter et l'attribut Shortener a l'attribut BasicShortener.

    ATTENTION : les attributs tweet et shorten doivent être du type de l'interface associée (tweet : Tweeter et shorten Shortener). Ces attributs doivent aussi avoir le même nom EXACTEMENT dans la classe tweetClient.

    Les beans

    Pour pouvoir injecter notre implémentation dans notre classes principale, Spring a besoin que cette dernière soit un bean : c'est à dire qu'elle doit avec des méthodes permettant de mettre en place l'implémentation. On appelle ces méthodes des getters et des setters.
    Cela nous donne donc pour notre classe TweetClient :
    private Tweeter tweet;
    private Shortener shorten;

    public Tweeter getTweet() {return tweet;}
    public void setTweet(Tweeter tweet) {this.tweet = tweet;}
    public Shortener getShorten() {return shorten;}
    public void setShorten(Shortener shorten) {this.shorten = shorten;}

    Ici nous avons 2 attributs tweet et shorten qui correspondent aux noms que nous avons mis dans le fichier de configuration xml. Ces attributs (qui sont des interfaces) peuvent avoir leurs implémentations via les méthodes set (setTweet et setShorten). Ce sont ces méthodes qui seront appelée par le framework Spring pour injecter les dépendances.

    L'ApplicationContext

    L'ApplicationContext est la classe de Spring qui va tout mettre ensemble. Cette dernière se charge en utilisant le fichier de configuration XML. L'applicationContext va ensuite nous fournir une instance des classes qui necessitent des injections.
    Cela nous donne donc pour notre Main :
    public static void main(String[] args) {
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       TweetClient tweeter = (TweetClient) context.getBean("tweetClient");
       tweeter.publishWithUrl("mon message de publication", "http://www.monsite.com/truccompliqué&dur_a_retenir=true");
    }

    Ici, en première ligne, nous chargeons le fichier de configuration beans.xml dans notre ApplicationContext. Ensuite nous demandons une instance de TweetClient et nous appelons ensuite simplement notre méthode de publication.
    Nous aurons en résultat selon le code mis dans BasicShortener et SmsTweeter notre message avec un lien simplifié vers monsite.com


    Pour faire de l'injection de dépendance, Spring IoC utilise aussi des annotations qui permettent de se passer d'une description en détails dans un fichier XML. Parmis ces annotations on peut trouver la plus connue @Autowired qui indique à Spring de chercher une implémentationpour l'injecter.

    Les sources de cet exemple sont disponibles ici