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.