Spring annotations vs standards annotations: Que choisir entre @Autowired et @Resource?

L'objet de ce billet, en deux parties, est de comparer l'annotation standard @Resource du package javax.annotation.Resource à celle de
spring @Autowired du package org.springframework.beans.factory.annotation.
Nous verrons les situations où nous sommes obligés de favoriser les annotations standards.

L'utilisation des annotations réduit considérablement la verbosité des fichiers de configuration de Spring.
C'est aussi le même constat pour d'autres frameworks.
En effet, le principe "convention Over configuration" participe à cette diffusion.
Certes, les "pour" et les "contre" ne manqueront pas d'arguments pour débattre.
Mais ceci n'est pas l'objet de ce billet.
L'objectif ici est de comparer ces deux types d'annotations.
Pour cela, nous nous appuyons sur un projet java simple, sans maven, que vous pouvez créer sous Eclipse en suivant les étapes décrites ci-après.
Nous écrivons d'abord un projet avec les annotations de Spring puis nous illustrons les difficultés qui nous amèneront à introduire les annotations standards.

PREMIERE PARTIE: Annotations Spring @Autowired, @Service & cie

Dans cette première partie, le projet java exemple ci-après n'utilise que les annotations de Spring.

Pré-requis:

Le projet java standard doit avoir les librairies nécessaires suivantes : spring-2.5.6.jar, spring-test-2.5.6.jar et Junit 4.4.jar ( apache-commons-**.jar).
Notez qu'il faudrait observer ces versions pour contourner un bug connu. Aussi, ne laissez pas eclipse choisir pour vous la librairie JUnit4 à ajouter dans le classpath du projet.

Configuration de Spring

Un fichier de Spring très léger nommé "spring-context.xml" qui contient ces deux lignes:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd" 
 default-autowire="byName">
<context:component-scan base-package="fr.netapsys.exemple.dao"/>	
<context:component-scan base-package="fr.netapsys.exemple.service"/>	
</beans>

Les classes java du projet sont ordinaires bien que les différentes couches sont bien séparées.

Couche entités

//la classe Entreprise 
package fr.netapsys.exemple.entites;
public class Entreprise implements Serializable{
	 private int id;
	 private boolean etat;
	 public void setEtat(boolean etat){
		this.etat=etat;		
	 } 
	 public boolean isEtat(){ return etat;}
	//autres setters/getters ......omis
	public String toString(){
		return "Entreprise (id="+id+" etat="+etat+")";
	}
}

Couche DAO

L'interface:

package fr.netapsys.exemple.dao;
public interface IEntrepriseDao{
	boolean createEntreprise(Entreprise e);
}

Notez l'annotation @Repository pour indiquer à Spring que cette classe appartient à la couche DAO.

Et l'implémentation donne:

package fr.netapsys.exemple.dao;
@Repository("entrepriseDao")
public class EntrepriseDao implements IEntrepriseDao{
 
	 public boolean createEntreprise(Entreprise entrep){
	 //do something...
            System.out.println("creating in dao ");
             return true;
	 }
}

Couche service

L'interface:

package fr.netapsys.exemple.service;
public interface IEntrepriseService{
	boolean createEntrerpise(Entrerpise entrep);
}

L'implémentation:

package fr.netapsys.exemple.service;
@Service("entrepriseService")
public class EntrepriseService{
 
	private IEntrepriseDao entrepriseDao;
	@Autowired
	public setEntrepriseDao()
	 public boolean createEntreprise(Entreprise entrep){
	  return entrepriseDao.createEntreprise(entreprise);
	 }
}

Notez l'annotation @Autowired sur le setter de l'attribut entrepriseDao.

Ajoutons une classe de JUnit 4.4 nommée TestAnnotations.java qui contient ceci:

package fr.netapsys.exemple.tests;
/*****imports omis*****/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-context.xml"}) 
 
public class TestAnnotationsSpring {
 
	private IEntrepriseService service;
	@Autowired
	public void setEntrepriseService(IEntrepriseService entrep){
		this.service=entrep;
	}
	@Test public void test1(){
		Entreprise entrep=new Entreprise();
		entrep.setId(1);
		boolean retour=service.createEntreprise(entrep);
		Assert.assertTrue(retour);
	} 
}

Observez l'annotation @RunWith de JUnit4 et celle de @ContextConfiguration de Spring.
Elles permettent d'exécuter ce test JUnit en laissant à Spring le soin de charger son contexte à partir des fichiers mentionnés dans locations.

L'exécution de ce test dans eclipse, clic-droit, run as Junit Test, donne le résultat :
junit_1

OK, tout cela fonctionne bien.

Mais si on rajoute une nouvelle classe de service, nommée EntrepriseServiceImpl2 dans la trace ci-dessous, qui implémente l'interface IEntrepriseService, notre test JUnit cesse de fonctionner!!!!
Et on obtient une "BeanCreationException" dont la trace est donnée ci-dessous:

''Autowiring of methods failed; nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire method:
public void fr.netapsys.exemples.tests.TestAnnotationsSpring.setEntrepriseService(fr.netapsys.exemple.service.IEntrepriseService); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type

  fr.netapsys.exemple.service.IEntrepriseService  is defined: expected single matching bean but found 2:
 entrepriseService, entrepriseServiceImpl2

at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues
............
.........
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type fr.netapsys.exemple.service.IEntrep... is defined:

   expected single matching bean but found 2: entrepriseService, entrepriseServiceImpl2

''

Pourquoi donc? Et comment contourner cela?

La seconde partie de ce billet donne avec détails la réponse à ces deux questions.

7 commentaires

  1. Howdy, Cool publish. There’s an trouble with your website around internet adventurer, can analyze this specific? For instance ‘s still industry chief along with a huge section other folks will probably miss your own wonderful publishing for this reason issue.

  2. Howdy, Cool publish. There’s an trouble with your website around internet adventurer, can analyze this specific? For instance ‘s still industry chief along with a huge section other folks will probably miss your own wonderful publishing for this reason issue.

Laisser un commentaire

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

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.