Publié par

Il y a 11 ans -

Temps de lecture 6 minutes

Simplifiez votre configuration Spring 2.5 avec les annotations

Spring 2.5 est sorti depuis le 19 novembre 2007 comme nous l’annoncions, il y a quelques temps, dans notre revue de presse. Vous avez comme moi sagement mis à jour vos poms Maven2 vers la dernière release de Spring (normalement et la plupart du temps compatible avec les versions 2.0.x). Mais avez-vous vraiment profité des nouveautés de cette version en terme de configuration ?

Les nouveautés Spring 2.5

Spring introduit plusieurs nouveautés concernant sa configuration, toutes les évolutions visant à la simplifier (ou à améliorer l’existant) :

  • Ajout de nouvelles balises basées sur les possibilités d’extension de schéma de Spring (Exemple : L’ajout des namespaces jms, context ou l’amélioration des namespaces jee, aop)
  • La création de JavaConfig
  • Le support des annotations Java 5, déjà amorcé dans Spring 2.0

Dans cet article, nous nous concentrerons sur les possibilités de configuration avec les annotations mais sans éviter un ou deux écarts :-) .

Configuration XML

Je vous parle d’annotations depuis le début de cet article, et le premier bout de code que je vous propose est en … XML. Eh oui, la prise en compte par Spring des annotations se configure encore en XML.

Mais heureusement avec l’extension de schéma ‘context’, cette configuration est très simple. Il suffit d’ajouter deux balises : la première sert à ajouter un certain nombre de BeanPostProcessor (voir la documentation), la seconde à définir le ou les packages (séparés par des virgules) que Spring va devoir parcourir à la recherche de nos annotations.



	
	


La balise <context:component-scan> n’est requise que si nous utilisons les annotations ‘stéréotype’ de Spring. Il est possible d’être plus directif sur la recherche d’annotations dans les classes comme le montre l’exemple ci-dessous.


   
   
   

Les stéréotypes Spring

Habituellement pour déclarer un bean géré par Spring, on devait ajouter une balise <bean name=...> dans notre fichier xml de configuration Spring. La balise <context:component-scan> vue précédemment indique à Spring qu’il doit rechercher dans le code certaines annotations que voici : @Repository, @Service, @Controller, @Component.

Ces annotations n’ont pas d’effet particulier sur l’injection de dépendances : elles servent entre autres à ‘catégoriser’ plus finement nos beans pour, en cas de besoin, appliquer des aspects plus facilement par la suite. Il faut voir ces annotations comme une bonne pratique à mettre en place pour donner du sens à nos beans.

  • @Repository : Cette annotation existe depuis Spring 2.0 et sert à identifier un bean de type DAO.
  • @Service : Celle-ci identifie le bean comme un service
  • @Controller : Utile uniquement si l’on utilise SpringMVC, cette annotation indique un contrôleur Spring MVC.
  • @Component : Cette dernière est l’annotation générique pouvant fonctionner pour n’importe quel bean

Autre avantage, Spring se sert de ces annotations pour appliquer lui aussi quelques aspects, comme par exemple, @Repository qui est pris en compte par Spring comme un marqueur pour la translation automatique d’exception pour la couche de persistance.

Voici un exemple très simple :

@Repository
public class ClientDAO {
    [...]
}

Chaque annotation prend un attribut ‘name’ optionnel qui peut être utilisé pour nommer explicitement le bean.

Ces annotations permettent de référencer nos beans dans le contexte d’application Spring pour qu’il puisse réaliser les injections de dépendances. Sans annotation, un bean n’est pas traité par Spring. Il existe une dernière annotation permettant de demander à Spring d’injecter des dépendances sur un bean qu’il ne gère pas : c’est l’annotation @Configurable. Spring ne va pas gérer le bean mais va quand même réaliser l’injection des dépendances sur celui-ci.

Pour modifier le cycle de vie de nos beans, il est aussi toujours possible de préciser le scope avec l’annotation du même nom : @Scope.

L’injection des dépendances

Nous venons de voir comment référencer nos beans dans le contexte d’application Spring. Nous allons étudier maintenant la façon de configurer l’injection de nos dépendances. Cela passe, bien sûr, par l’ajout de quelques annotations dans notre code. Il y a deux catégories d’annotations : les standards et les propriétaires Spring.

Le support de la JSR-250 et de JPA

La JSR-250 introduit plusieurs annotations permettant de déclarer un besoin d’injection de dépendances. Spring supporte l’annotation @Resource permettant la déclaration d’une ressource à injecter (cette annotation peut prendre l’attribut ‘name’ en paramètre). Dans la terminologie Spring, ce type d’injection est appelée ‘by-name’. Pour déclarer une dépendance comme obligatoire, on ajoutera l’annotation @Required. Dans cette JSR, il existe deux autres annotations prises en charge par Spring : @PreConstruct et @PostDestroy qui remplace l’utilisation des interfaces InitializingBean et DisposableBean.

Ces annotations sont disponibles en standard dans le jdk6. Si vous utilisez le jdk5, vous devrez obtenir le jar à cet url ou si vous utilisez maven, la dépendance à ajouter est la suivante javax.annotation:jsr250-api:1.0.

JPA de son côté introduit lui aussi quelques annotations supportées par Spring : @PersistenceUnit qui permet l’injection d’un EntityManagerFactory et @PersistenceContext (ou @PersistentContext{+}s+) pour l’injection d’un EntityManager.

Les annotations spécifiques Spring

Spring permet aussi l’utilisation de l’annotation @Autowired qui lui est propre. Cette annotation fonctionne comme le @Resource sauf qu’ici Spring fera de l’injection ‘by-type’ c’est à dire en se basant sur la classe du bean à injecter. Il n’est ici pas possible de préciser un nom de bean explicitement. Il est par contre possible d’utiliser l’annotation @Qualifier pour permettre à Spring de choisir la bonne dépendance. Avec cette annotation on précise des qualifiers à la fois sur les beans managés et sur les attributs ou sur les méthodes où Spring doit injecter des dépendances : Spring trouvera le meilleur candidat à l’injection en s’aidant de ces qualifiers si besoin.

Pour déclarer une dépendance comme obligatoire, on utilisera l’attribut required de l’annotation @Autowired.

Pour faciliter le travail du transaction manager, l’annotation @Transactional peut être positionnée sur une classe ou sur les méthodes qui la composent pour préciser à Spring quelles méthodes doivent s’exécuter dans une transaction (sans oublier d’ajouter <tx:annotation-driven transaction-manager= »txManager »/> avec votre fichier de configuration XML). L’annotation prend plusieurs attributs permettant de configurer le comportement transactionnel à appliquer.

A-t-on encore besoin de fichier XML ?

Avec l’introduction des annotations, a-t-on encore besoin de fichiers XML pour configurer Spring ? La réponse est bien sûr ‘oui’ puisqu’il reste nécessaire pour configurer la prise en compte des annotations. Cependant, ces annotations permettent de beaucoup simplifier et d’alléger le fichier de configuration. Seul le strict nécessaire subsistera : la configuration et la déclaration des ressources communes à tous les beans ainsi que la création des beans que nous ne développons pas (et donc sur lesquels nous ne pouvons pas positionner d’annotations).

Notamment, la configuration et l’utilisation d’AOP ainsi que la déclaration du transaction manager, d’Hibernate ou de JPA sont les éléments qui peuvent rester dans le fichier de configuration XML classique.

Ressources

Publié par

Commentaire

6 réponses pour " Simplifiez votre configuration Spring 2.5 avec les annotations "

  1. Publié par , Il y a 11 ans

    Bonjour,

    Je trouve votre article très instructif. Toutefois, je suis plutôt allergique aux annotations en règle générale à cause de l’obscurcissement du code induit.
    Dans votre dernier paragraphe vous indiquer que l’AOP ou JPA « peuvent » rester dans le fichier XML. Pourquoi ?
    Selon moi, les annotations n’améliorent pas la lisibilité du code, surtout lorsqu’on passe tous les frameworks (et ils sont nombreux au sein d’un projet : W-S, ORM, MVC, AOP, etc.) en annotations. De plus, la configuration d’un framework gagne à ne pas être éparpiller dans le code, mais centralisée dans un fichier de configuration, non ?
    De plus, lorsque je vois des beans contenant des infos relevant de problématiques techniques transversales et non métier, je me dis qu’on retourne dans la philosophie EJB de pollution du code.

    Que pensez-vous plus généralement des annotations, dans quelle(s) situation(s) pensez-vous qu’elles apportent une réelle valeur ajoutée ?

    Excellent blog BTW, continuez comme ça :-)

  2. Publié par , Il y a 11 ans

    Bonjour,

    Excellent article, comme toujours chez Xebia :-)

    Pour répondre à Philippe, il est toujours possible de configurer aspectj ou Hibernate en utilisant une configuration XML.
    Pour aspectj, en particulier, cela peut-être plus simple d’utiliser des annotations, mais il est vrai que cela « pollue » un peu de le code, comme vous dites.

    En fait, nous proposons les deux options (XML ou annotations), et même de faire un mix des deux suivant vos besoins. Ceci dit, nous proposons un framework générique avec de nombreuses options, à vous de voir ce qui est le mieux pour votre projet.

    C’est à ce niveau que toute l’expertise d’une société telle que Xebia (par ailleurs partenaire SpringSource) peut vous aider : nous fournissons les outils, ils vous aident à les utiliser au mieux.

    Cordialement,

    Julien Dubois.

  3. Publié par , Il y a 11 ans

    Merci à Xebia pour cet article fort intéressant.

    Pour l’injection de dépendance, j’utilise quasi exclusivement l’annotation standard @Resource qui permet en fait de faire aussi bien de l’injection par type que par nom. La règle semble être la suivante:
    * Si le nom de bean peut être résolu, il est utilisé.
    * Sinon un lookup par type est fait et, si une instance unique du type voulu existe, elle est injectée.

    Le nom du bean est résolu au choix:
    * Avec la valeur de l’attribut name de l’annotation @Resource.
    * Avec le nom du champs ou de la propriété JavaBean décorée par l’annotation.

    Quant au nom d’un bean déclaré à l’aide d’une annotation @Repository/@Controller/@Component/@Service, il est égal:
    * A la valeur de l’attribut value de l’annotation si elle est renseignée.
    * Au nom simple de la classe avec la première lettre en casse réduite.

    Ainsi, on pourra

    // Defaults to « fooAction » bean name
    @Component
    public class FooAction implements Action {

    }

    @Component(« notFooAction »)
    public class BarAction implements Action {

    }

    @Component
    public class MyUniqueActionBar {

    // Resolves injection from the field name
    @Resource
    private Action fooAction;

    // Resolves injection from the annotation attribute value
    // We inject the same instance here!
    @Resource(« fooAction »)
    private Action anotherFooAction

    // Inject from the attribute value
    @Resource(« notFooAction »)
    private Action anotherBarAction;

    // WARNING!! This last injection will NOT work
    // No bean name matches
    // And no unique instance of type Action exists
    @Resource
    private Action barAction;

    }

    public class MyFrame extends JFrame {

    // Will work if we assume there is only one bean of type MyUniqueActionBar
    @Resource
    private MyUniqueActionBar actionBar

    }

    Pour avoir réalisé des portions de code amenées à changer très fréquemment, cette méthode réduit énormément le nombre de lignes pour déclarer et injecter nos beans. Cela est particulièrement utile en prototypage.

    Si la base de code devient particulièrement importante et que des beans déclarés par annotations portent le même nom simple de classe (rendant inutile la génération de nom automatique), il peut être intéressant d’utiliser des constantes pour expliciter le nom des beans (cette solution a ses propres inconvénients en termes de découplage).

    Ainsi:

    package warehousing;
    public interface FooService {

    String BEAN_NAME = « warehousing.fooService »;

    }

    package usermanagement;
    public interface FooService {

    String BEAN_NAME = « userManagement.fooService »;

    ..
    }

    @Service(usermanagement.MyService.BEAN_NAME)
    public class MyFooService implements usermanagement.FooService {

    ..

    }

    @Controller
    public class MyController {

    @Resource(name=usermanagement.FooService.BEAN_NAME)
    private usermanagement.FooService usermanagementFooService;

    @Resource(name=warehousing.FooService.BEAN_NAME)
    private warehousing.FooService warehousingFooService;

    }

    Merci à Xebia pour cet article!

  4. Publié par , Il y a 11 ans

    @Philippe : Il est vrai qu’il y a un alourdissement du code mais on y gagne aussi puisque les informations sont disponibles dans la classe elle-même plutôt que dans un fichier XML qui a tendance à grossir. Je dirais que c’est un mal pour un bien …
    Selon les frameworks, la centralisation dans un fichier XML n’apporte pas forcement grand chose.
    Pour allèger le code des annotations, il est aussi possible de créer nos propres annotations qui permettent d’injecter en AOP d’autres annotations. On pourraient imaginer une annotation spécifique @BusinessService qui regrouperaient le @Transactionnal, le @Remote, (par exemple) etc…

    @Olivier : L’autre possibilité prise en charge par Spring est d’utiliser l’annotation @Qualifier qui permet de donner un qualificatif à une classe et de différencier deux classes en cas de conflit. Dans votre exemple, vous auriez pu ajouter un @Qualifier(value= »userManagement ») et @Qualifier(value= »wareHousing ») par exemple.

    Merci pour vos encouragements !
    Manuel@Xebia

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.