Spring: Configurer plusieurs contextes Spring

Ce billet fournit un exemple pratique de gestion de deux contextes Spring dont un est programatique.

En d'autres termes, le premier contexte est défini de manière classique et le second est chargé dynamiquement.

Ces deux contextes réunis définissent un contexte riche pour notre démo.

Spring v3.x est utilisé.

Passons à la démo décomposée en deux parties.
La première est simple et sert uniquement à valider la construction pour la seconde.

I. ETAPE 1: Définir le pom du projet

<dependencies>
 <!--  spring  -->	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
		<version>${spring.framework.version}</version>
	</dependency>	
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.8</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>${spring.framework.version}</version>
		<scope>test</scope>
	</dependency>
</dependencies>

II. ETAPE 2: Définir le premier fichier xml de configuration de spring

Le premier fichier xml de configuration nommé spring-context.xml.
Il se trouve dans sr/main/resources/META-INF. Son contenu est:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.s.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.s.org/schema/context"
xsi:schemaLocation="http://www.s.org/schema/beans 
http://www.s.org/schema/beans/spring-beans-3.0.xsd
http://www.s.org/schema/context 
http://www.s.org/schema/context/spring-context-3.0.xsd">		
 <bean id="contact" class="fr.moi.spring.Contact">
   <property name="name" value="Toto"/>
 </bean>	
 <context:component-scan base-package="fr.moi.spring" />
</beans>

On initialise un bean simple nommé contact ayant un seul attribut nommé name.

L'interface IContientService contient une seule méthode :

String getName();

L'implémentation ContactService est donnée ci-après:

package fr.moi.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ContactService implements IContactService {
  @Autowired
  private Contact contact;
  @Override
  public String getName() {
	 return contact.getName();
  }
}

III. ETAPE 3: Premier test JUnit

Pour valider la construction de notre projet, écrivons ce test JUnit.

package fr.moi.spring;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
          "classpath:/META-INF/spring-context.xml"})
public class Test1CtxClassic {
 @Autowired 
 IContactService contactService;
 @Test
 public void test1Ctx()  {
  assertTrue("Toto".equals(contactService.getName()));
 }
}

L'exécution du test Junit dans eclipse ou en console :

 mvn test

permet de s'assurer que tout est ok pour l'instant.

Ceci étant validé passons à la seconde partie (un peu avancée).

IV. ETAPE 4: Chargement dynamique du contexte Spring

Le code ci-après commence par instantier un BeanFactory.

En spring 3.x, cela se fait exactement via DefaultListableBeanFactory.

// Step1. Créer un BeanFactory
 DefaultListableBeanFactory dlbf = new DefaultListableBeanFactory();
// Step2. Créer un reader  
 XmlBeanDefinitionReader xbdr=new XmlBeanDefinitionReader(dlbf);
//Step3.  Charger effectivement les beans à partir d'un flux
 xbdr.loadBeanDefinitions(
      new InputStreamResource(new FileInputStream(..TODO...))
   );
//Step4. PréCharger explicitement les singletons
 dlbf.preInstantiateSingletons();

Vous constatez que le code est incomplet!

Pensez à remplacer le TODO par le chemin vers le flux xml contenant les beans à charger (Voir étape suivante).

Ainsi, en quatre étapes (step1 à step4) nous avons chargé dynamiquement un contexte spring.

A l'étape step1, notez la création du BeanFactory nommé dlbf avec le constructuer par défaut.
Sachez néanmoins que l'on peut le lier à un contexte spring parent via le constructeur approprié.
En effet, l'un des contructeurs de DefaultListableBeanFactory prend un argument de type ApplicationContext ou DefaultListableBeanFactory.

Ces lignes de code java seront consignées dans GenericCtxSpring.java fourni en annexe.

A l'étape suivante nous donnons le contenu xml stocké sur disque.
Dans notre exemple, le fichier est nommé demo.xml et stocké dans /temp.

V. ETAPE 5: Création du contenu xml sur disque

Voici un exemple du contenu du fichier xml localisé dans /temp/spring.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans ........
	<bean id="contact2" class="fr.moi.spring.Contact">
		<property name="name" value="TATA100"/>
	</bean>	
	<bean id="contact3" class="fr.moi.spring.Contact">
		<property name="name" value="TITI101"/>
	</bean>
</beans>

VI. ETAPE 6: Second test JUnit

package fr.moi.spring;
import static org.junit.Assert.assertTrue;
import java.io.FileNotFoundException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
         "classpath:/META-INF/spring-context.xml"})
public class Test2Ctx {
@Autowired IContactService contactService;
@Test
public void test2Ctx() throws FileNotFoundException  {
 assertTrue("Toto".equals(contactService.getName()));
 GenericCtxSpring gCtx=new GenericCtxSpring("/temp/spring.xml", null);
 Contact c=gCtx.getBean("contact2", Contact.class);
 assertTrue("TATA100".equals(c.getName()));
}

Le test Test2Ctx définit un contexte nommé gCtx qui dans ce cas n'est lié à aucun autre.
Le code du test permet d'obtenir des beans définis dans /temp/spring.xml.

VI. ANNEXE.

Vous pouvez télécharger la classe genericCtxSpring GenericCtxSpring.zip.

Un commentaire

  1. @Springer

    Merci,

    La réponse à la question a été évoquée dans l’introduction (chargement dynamique du contexte programmatique).

    La déclaration statique (xml-centric ou java-centric) du contexte spring vs la déclaration programmatique (dynamique).

    Le contenu programmatique ne sera validé par spring qu’au runtime.

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.