Le conteneur Spring propose plusieurs mécanismes pour instancier les composants applicatifs. Le choix de la stratégie dépend des contraintes architecturales et de la complexité de la création de l'objet.
1. Instanciation par constructeur par défaut
Lorsqu'une déclaration de bean ne précise aucun mécanisme de fabrication, Spring utilise implicitement le constructeur vide de la classe ciblée. Au démarrage du contexte d'application, le framework charge la classe via l'API de réflexion Java et invoque newInstance(). Cette approhce impose la présence d'un constructeur sans arguments ; en son absence, une BeanInstantiationException sera levée.
package com.example.model;
public class MessageService {
public MessageService() {
System.out.println("Initialisation via constructeur par défaut.");
}
public void dispatch() {
System.out.println("Distribution du message.");
}
}
Déclaration contextuelle :
<bean id="msgSvc" class="com.example.model.MessageService"/>
Récupération dans le test :
ApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
MessageService service = context.getBean("msgSvc", MessageService.class);
service.dispatch();
2. Délégation à une méthode d'usine statique
Certaines classes ne peuvent pas être construites directement (absence de constructeur accessible, classe abstraite, ou logique de création externalisée). Dans ce cas, Spring peut invoquer une méthode statique d'une classe utilitaire pour produire le bean. Aucune instance de la classe utilitaire n'est créée par le conetneur.
package com.example.util;
import java.time.Clock;
public class SystemClockFactory {
public static Clock provide() {
return Clock.systemDefaultZone();
}
}
Configuration XML :
<bean id="appClock" class="com.example.util.SystemClockFactory" factory-method="provide"/>
Validation :
ApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
Clock clock = context.getBean("appClock", Clock.class);
System.out.println(clock.instant());
3. Délégation à une méthode d'instance
Si la méthode de fabrication n'est pas statique, le conteneur doit d'abord instancier la fabrique elle-même, puis appeler la méthode cible sur cette instance spécifique. Cela nécessite deux déclarations : une pour la fabrique et une pour le produit.
package com.example.provider;
import java.util.UUID;
public class TokenIssuer {
public UUID issueToken() {
return UUID.randomUUID();
}
}
Configuration XML :
<bean id="issuer" class="com.example.provider.TokenIssuer"/>
<bean id="activeToken" factory-bean="issuer" factory-method="issueToken"/>
Utilisation :
ApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
UUID token = context.getBean("activeToken", UUID.class);
System.out.println("Token actif : " + token);
4. Implémentation de l'interface FactoryBean
Pour les scénarios de fabrication avancés, Spring fournit l'interface FactoryBean. En l'implémentant, le développeur contrôle entièrement la logique de création, le type retourné et le cycle de vie (singleton ou prototype). Lors de la demande du bean, le conteneur invoque automatiquement la méthode getObject().
package com.example.spring;
import org.springframework.beans.factory.FactoryBean;
import java.time.LocalDate;
public class DateSupplier implements FactoryBean<LocalDate> {
@Override
public LocalDate getObject() throws Exception {
return LocalDate.now();
}
@Override
public Class<?> getObjectType() {
return LocalDate.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
Déclaration :
<bean id="currentDate" class="com.example.spring.DateSupplier"/>
Consommation :
ApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
LocalDate date = context.getBean("currentDate", LocalDate.class);
System.out.println(date);