Spring3 : Combiner les modes de configuration du conteneur

Comme l'indique le titre, à l'aide d'une démo réaliste on montre comment combiner les trois modes de configuration de Spring 3.
Précisément, on illustre comment la classe annotée @Configuration gère aussi les beans déclarés dans un fichier xml ou par
les annotations (@Component ou ses dérivées).
Pourquoi ce mélange ?
Je vous l'accorde, ce n'est pas l'idéal!
Néanmoins, c'est un cas de figure réel quand on récupère un projet existant.
Ce billet combine les trois configurations et prouve que tout fonctionne parfaitement avec une seule précaution.
Laquelle ?
La suite du billet en apporte la réponse.

Ce premier projet, java standard, permet d'entrer directement dans la pratique :
Dans un billet précédent, la marche à suivre pour créer le projet maven a été donné. Merci d'y revenir si besoin.

Etape1. Écrire le fichier xml de configuration nommé spring-context.xml:

 <bean id="service" class="fr.netapsys.tb.services.UserService"/>

Etape2. Écrire la classe java nommée UserService:

public class UserService implements IUserService{
 public String userInfos() {
 	return "infos";
 }
}

Noter que l'interface IUserService ne contient qu'une signature : String userInfos();

Etape3. Écrire une classe java nommée AppConfig:


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import fr.netapsys.tb.services.IUserService;
import fr.netapsys.tb.services.UserService;
@Configuration 
@ImportResource("classpath:spring-context.xml")
public class AppConfig {	
	@Bean 
	public IUserService userService() {
		return new UserService();
	}
}

Rappelons que cette classe est destinée à configurer Spring.
La ligne importante est celle commençant par @ImportResource
Ceci a pour conséquence que les beans déclarés par xml sont inclus dans le bean AppConfig (oui c'est un bean de Spring comme les autres et il aura l'id=appConfig).
Il résulte que le conteneur (pré)configure/instancie déjà nos beans ayant les id: appConfig, service et userService.

Ci-dessous, une autre manière d'écrire la classe AppConfig en explicitant tous les beans qui seront appelés dans le projet:

@Configuration 
@ImportResource("classpath:spring-context.xml")
public class AppConfig {	
 @Autowired @Qualifier("service")
 private IUserService service;
	
 @Bean 
 public IUserService userService() {
 	return new UserService();
 }

 @Bean public IUserService userService2(){
	return service;
 }
}

Le bean "service" déclaré dans le xml est bien géré dans la classe AppConfig.
A noter que la seconde méthode est nommée "userService2".
Enfin, l'utilisation de @Qualifier est nécessaire pour qualifier sans ambiguïté le bean qui sera "autowired" (auto-câblé).

Etape4. Écrire un test JUnit:

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import fr.netapsys.tb.cfg.AppConfig;
import fr.netapsys.tb.services.IUserService;
public class TestCfgConteneur {
    private static ApplicationContext ctx;
    @BeforeClass public static void avantTout(){
	ctx=new AnnotationConfigApplicationContext(AppConfig.class);	
     }
    @Test public void testCfg(){
	IUserService serv=ctx.getBean("userService",IUserService.class);
	Assert.assertTrue(serv.userInfos().contains("infos"));		
	serv=ctx.getBean("service",IUserService.class);
        // ou serv=ctx.getBean("userService2",IUserService.class);
	Assert.assertTrue(serv.userInfos().contains("infos"));
     }
     @AfterClass public static void apresTout(){
	ctx=null;
     }
}

Dans la méthode JUnit annotée @BeforeClass, la propriété "ctx" est initialisée à l'aide de l'instruction:

ctx=new AnnotationConfigApplicationContext(AppConfig.class);

"AnnotationConfigApplicationContext" est l'une des implémentations de l'interface ApplicationContext.
Le constructeur avec argument de type Class de "AnnotationConfigApplicationContext" appelé ici l'argument est AppConfig.class.

Etape5. Exécuter le test JUnit:

L'exécution du test affiche la barre verte et les traces 'extraits):

INFO (XmlBeanDefinitionReader.java:315) Loading XML bean definitions from class path resource spring-context.xml
INFO (DefaultListableBeanFactory.java:557) Pre-instantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@3c3c9217:
defining beans ... __appConfig__,__userService__,...; root of factory hierarchy

En gras nos beans chargés par Spring.
Donc c'est aussi facile de combiner les modes possibles de configuration de Spring.
La seule précaution (qui était déjà de rigueur) est de veiller à l'unicité des id des beans de Spring.

Laisser un commentaire

Votre adresse de messagerie 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.