Section courante

A propos

Section administrative du site

Le conteneur IoC

Cette page décrit l'implémentation du principe d'inversion de contrôle (IoC) dans le cadre d'application Spring. L'injection de dépendances (DI) est une forme spécialisée d'IoC, où les objets définissent leurs dépendances (c'est-à-dire les autres objets avec lesquels ils interagissent) uniquement via les arguments de leur constructeur, les arguments d'une méthode de fabrique ou les propriétés définies sur l'instance de l'objet après sa construction ou son retour par une méthode de fabrique. Le conteneur IoC injecte ensuite ces dépendances lors de la création du bean. Ce processus est fondamentalement l'inverse (d'où le nom d'inversion de contrôle) du comportement où le bean contrôle l'instanciation ou l'emplacement de ses dépendances par la construction directe de classes ou un mécanisme tel que le modèle Service Locator.

Les paquets `org.springframework.beans` et `org.springframework.context` constituent la base du conteneur IoC du cadre d'application Spring. L'interface `BeanFactory` fournit un mécanisme de configuration avancé capable de gérer tout type d'objet. `ApplicationContext` est une sous-interface de `BeanFactory`. Il ajoute :

En résumé, BeanFactory fournit le cadre de configuration et les fonctionnalités de base, tandis qu'ApplicationContext ajoute des fonctionnalités plus spécifiques aux entreprises. ApplicationContext est un sur-ensemble complet de BeanFactory et est utilisé exclusivement dans ce chapitre pour décrire le conteneur IoC de Spring. Pour plus d'informations sur l'utilisation de BeanFactory à la place d'ApplicationContext, consultez la section relative à l'API BeanFactory.

Dans Spring, les objets constituant l'ossature de votre application et étant gérés par le conteneur IoC de Spring sont appelés beans. Un bean est un objet instancié, assemblé et géré par un conteneur IoC de Spring. Autrement, un bean est simplement un objet parmi d'autres dans votre application. Les beans, ainsi que leurs dépendances, sont reflétés dans les métadonnées de configuration utilisées par un conteneur.

Présentation du conteneur

L'interface `org.springframework.context.ApplicationContext` représente le conteneur IoC de Spring et est responsable de l'instanciation, de la configuration et de l'assemblage des beans. Le conteneur obtient les instructions relatives aux composants à instancier, configurer et assembler en lisant les métadonnées de configuration. Ces métadonnées peuvent être représentées par des classes de composants annotées, des classes de configuration avec des méthodes de fabrique, ou des fichiers XML externes ou des scripts Groovy. Quel que soit le format utilisé, vous pouvez composer votre application et gérer les interdépendances complexes entre ces composantes.

Plusieurs implémentations de l'interface `ApplicationContext` font partie intégrante de Spring. Dans les applications autonomes, il est courant de créer une instance de `AnnotationConfigApplicationContext` ou de `ClassPathXmlApplicationContext`.

Dans la plupart des cas d'utilisation, aucun code utilisateur explicite n'est requis pour instancier un ou plusieurs conteneurs IoC de Spring. Par exemple, dans une application web classique, un simple descripteur web XML standard dans le fichier `web.xml` de l'application suffit. Dans un scénario Spring Boot, le contexte d'application est initialisé automatiquement selon des conventions de configuration courantes.

Le diagramme suivant présente une vue d'ensemble du fonctionnement de Spring. Vos classes d'application sont combinées aux métadonnées de configuration afin qu'après la création et l'initialisation de l'ApplicationContext, vous disposiez d'un système ou d'une application entièrement configuré(e) et exécutable :

Métadonnées de configuration

Comme le montre le schéma précédent, le conteneur IoC Spring utilise des métadonnées de configuration. Ces métadonnées indiquent comment, en tant que développeur, vous configurez le conteneur Spring pour qu'il instancie, configure et assemble les composants de votre application.

Le conteneur IoC Spring est totalement indépendant du format de ces métadonnées. De nos jours, de nombreux développeurs optent pour une configuration Java pour leurs applications Spring :

La configuration Spring comprend au moins une, et généralement plusieurs, définitions de beans que le conteneur doit gérer. La configuration Java utilise généralement des méthodes annotées avec `@Bean` au sein d'une classe `@Configuration`, chacune correspondant à une définition de bean.

Ces définitions de beans correspondent aux objets constituant votre application. On y définit généralement des objets de la couche de service, des objets de la couche de persistance tels que des dépôts ou des objets d'accès aux données (DAO), des objets de présentation tels que des contrôleurs Web, des objets d'infrastructure tels qu'une `EntityManagerFactory` JPA, des files d'attente JMS,... En règle générale, on ne configure pas les objets de domaine de manière précise dans le conteneur, car il incombe généralement aux dépôts et à la logique métier de créer et de charger ces objets.

XML comme DSL de configuration externe

Les métadonnées de configuration basées sur XML configurent ces beans sous forme d'éléments `<bean/>` à l'intérieur d'un élément `<beans/>` de niveau supérieur. L'exemple suivant illustre la structure de base des métadonnées de configuration XML :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
          https://www.springframework.org/schema/beans/spring-beans.xsd">

     <bean id="..." class="...">  
          <!-- Les collaborateurs et la configuration de ce bean se trouvent ici. -->
     </bean>

     <bean id="..." class="...">
          <!-- Les collaborateurs et la configuration de ce bean se trouvent ici. -->
     </bean>

     <!-- Plus de définitions de haricots ici -->

</beans>

La valeur de l'attribut `id` permet de faire référence à des objets collaboratifs. Le code XML correspondant n'est pas présenté dans cet exemple.

Pour instancier un conteneur, il est nécessaire de fournir le ou les chemins d'accès aux fichiers de ressources XML au constructeur `ClassPathXmlApplicationContext`. Ce constructeur permet au conteneur de charger les métadonnées de configuration à partir de diverses ressources externes, telles que le système de fichiers local, le CLASSPATH de Java,...

Voici un exemple de code Java :

  1. ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

Voici un exemple de code Kotlin :

  1. val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")    

Après avoir découvert le conteneur IoC de Spring, vous souhaiterez peut-être en savoir plus sur l'abstraction Resource de Spring, offrant un mécanisme pratique pour lire un InputStream à partir d'emplacements définis par une syntaxe URI. Plus précisément, les chemins de ressources servent à construire les contextes d'application.

L'exemple suivant illustre le fichier de configuration des objets de la couche de service (services.xml) :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
          https://www.springframework.org/schema/beans/spring-beans.xsd">

     <!-- services -->

     <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
          <property name="accountDao" ref="accountDao"/>
          <property name="itemDao" ref="itemDao"/>
          <!-- Pour plus de collaborateurs et la configuration de ce beanrendez-vous ici. -->
     </bean>

     <!-- Plus de définitions de beans pour les services sont disponibles ici. -->

</beans>

L'exemple suivant illustre le fichier daos.xml des objets d'accès aux données :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
          https://www.springframework.org/schema/beans/spring-beans.xsd">

     <bean id="accountDao"
          class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
          <!-- Pour plus de collaborateurs et la configuration de ce beanrendez-vous ici. -->
     </bean>

     <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
          <!-- Pour plus de collaborateurs et la configuration de ce beanrendez-vous ici. -->
     </bean>

     <!-- Vous trouverez ici davantage de définitions de beans pour les objets d'accès aux données. -->

</beans>

Dans l'exemple précédent, la couche de service est composée de la classe PetStoreServiceImpl et de deux objets d'accès aux données de types JpaAccountDao et JpaItemDao (conformes à la norme JPA). L'élément `id` fait référence au nom de la propriété JavaBean, et l'élément `ref` fait référence au nom d'une autre définition de bean. Ce lien entre les éléments `id` et `ref` exprime la dépendance entre les objets collaborant. Pour plus d'informations sur la configuration des dépendances d'un objet, consultez la section Dépendances.

Création de métadonnées de configuration XML

Il peut être utile de répartir les définitions de beans sur plusieurs fichiers XML. Souvent, chaque fichier de configuration XML représente une couche logique ou un module de votre architecture.

Vous pouvez utiliser le constructeur `ClassPathXmlApplicationContext` pour charger les définitions de beans à partir de fragments XML. Ce constructeur accepte plusieurs emplacements de ressources, comme indiqué dans la section précédente. Vous pouvez également utiliser une ou plusieurs occurrences de l'élément `<import/>` pour charger les définitions de beans à partir d'un ou plusieurs autres fichiers. L'exemple suivant illustre cette procédure :

<beans>
     <import resource="services.xml"/>
     <import resource="resources/messageSource.xml"/>

     <bean id="bean1" class="..."/>
     <bean id="bean2" class="..."/>
</beans>

Dans l'exemple précédent, les définitions de beans externes sont chargées à partir des fichiers `services.xml` et `messageSource.xml`. Tous les chemins d'accès sont relatifs au fichier de définition effectuant l'importation. Par conséquent, `services.xml` doit se trouver dans le même répertoire (ou le même classpath) que le fichier importateur, tandis que `messageSource.xml` doit se trouver dans un répertoire de ressources situé en dessous de celui du fichier importateur. Comme vous pouvez le constater, la barre oblique initiale est ignorée. Cependant, étant donné que ces chemins sont relatifs, il est préférable de ne pas utiliser de barre oblique du tout. Le contenu des fichiers importés, y compris l'élément racine `<beans/>`, doit être constitué de définitions de beans XML valides, conformément au schéma Spring.

L'espace de noms lui-même fournit la fonctionnalité de directive d'importation. D'autres options de configuration, au-delà des simples définitions de beans, sont disponibles dans une sélection d'espaces de noms XML fournis par Spring - par exemple, les espaces de noms context et util.

Utilisation du conteneur

L'ApplicationContext est l'interface d'une fabrique avancée capable de gérer un registre de différents beans et de leurs dépendances. La méthode `T getBean(String name, Class<T> requiredType)` permet de récupérer des instances de vos beans.

L'ApplicationContext permet de lire les définitions des beans et d'y accéder, comme le montre l'exemple Java suivant :

  1. // créer et configurer des beans
  2. ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
  3.  
  4. // récupérer l'instance configurée
  5. PetStoreService service = context.getBean("petStore", PetStoreService.class);
  6.  
  7. // utiliser l'instance configurée
  8. List<String> userList = service.getUsernameList();

ou l'exemple Kotlin suivant :

  1. // créer et configurer des beans
  2. val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
  3.  
  4. // récupérer l'instance configurée
  5. val service = context.getBean<PetStoreService>("petStore")
  6.  
  7. // utiliser l'instance configurée
  8. var userList = service.getUsernameList()    

Avec la configuration Groovy, l'initialisation est très similaire. Elle utilise une classe d'implémentation de contexte différente, compatible avec Groovy (mais comprenant également les définitions de beans XML). L'exemple suivant illustre la configuration Groovy :

  1. ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

ou l'exemple Kotlin suivant :

  1. val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")

La variante la plus flexible est GenericApplicationContext en combinaison avec des délégués de lecture - par exemple, avec XmlBeanDefinitionReader pour les fichiers XML, comme le montre l'exemple Java suivant :

  1. GenericApplicationContext context = new GenericApplicationContext();
  2. new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
  3. context.refresh();     

ou l'exemple Kotlin suivant :

  1. val context = GenericApplicationContext()
  2. XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
  3. context.refresh()

Vous pouvez également utiliser GroovyBeanDefinitionReader pour les fichiers Groovy, comme le montre l'exemple suivant :

  1. GenericApplicationContext context = new GenericApplicationContext();
  2. new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
  3. context.refresh();     

ou l'exemple Kotlin suivant :

  1. val context = GenericApplicationContext()
  2. GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
  3. context.refresh()

Vous pouvez combiner différents délégués de lecture sur le même ApplicationContext, en lisant les définitions de beans provenant de diverses sources de configuration.

Vous pouvez ensuite utiliser `getBean` pour récupérer des instances de vos beans. L'interface `ApplicationContext` propose d'autres méthodes pour récupérer des beans, mais idéalement, votre code applicatif ne devrait jamais les utiliser. En effet, votre code applicatif ne devrait jamais appeler la méthode `getBean()` et donc ne dépendre d'aucune API Spring. Par exemple, l'intégration de Spring avec les cadres d'applications web fournit l'injection de dépendances pour divers composantes de cadres d'applications web, tels que les contrôleurs et les beans gérés par JSF, vous permettant de déclarer une dépendance à un bean spécifique via des métadonnées (comme une annotation d'injection automatique).

Aperçu de Bean

Un conteneur Spring IoC gère un ou plusieurs beans. Ces beans sont créés à partir des métadonnées de configuration que vous fournissez au conteneur (par exemple, sous forme de définitions XML `<bean/>`).

Au sein du conteneur, ces définitions de beans sont représentées par des objets `BeanDefinition`, qui contiennent (entre autres informations) les métadonnées suivantes :

Ces métadonnées se traduisent par un ensemble de propriétés constituant la définition de chaque bean. Le tableau suivant décrit ces propriétés :

Propriété Explication dans...
Classe Instanciation des beans
Nom Nommage des beans
Portée Portée des beans
Arguments du constructeur Injection de dépendances
Propriétés Injection de dépendances
Mode d'injection automatique Injection automatique des collaborateurs
Mode d'initialisation paresseuse Beans à initialisation paresseuse
Méthode d'initialisation Fonctions de rappel d'initialisation
Méthode de destruction Fonctions de rappel de destruction

Outre les définitions de beans contenant les informations nécessaires à la création d'un bean spécifique, les implémentations d'ApplicationContext permettent également l'enregistrement d'objets existants créés en dehors du conteneur (par les utilisateurs). Pour ce faire, on accède à la BeanFactory d'ApplicationContext via la méthode `getAutowireCapableBeanFactory()`, qui retourne l'implémentation `DefaultListableBeanFactory`. Cette dernière prend en charge cet enregistrement via les méthodes `registerSingleton(..)` et `registerBeanDefinition(..)`. Cependant, les applications classiques fonctionnent exclusivement avec des beans définis par des métadonnées de définition de bean classiques.

Les métadonnées des beans et les instances singleton fournies manuellement doivent être enregistrées le plus tôt possible afin que le conteneur puisse les interpréter correctement lors de l'injection automatique et d'autres étapes d'introspection. Bien que la surcharge des métadonnées et des instances singleton existantes soit prise en charge dans une certaine mesure, l'enregistrement de nouveaux beans à l'exécution (simultanément à l'accès en direct à la fabrique) n'est pas officiellement pris en charge et peut entraîner des exceptions d'accès concurrent, un état incohérent dans le conteneur de beans, ou les deux.

Surcharge de beans

La surcharge de beans se produit lorsqu'un bean est enregistré avec un identifiant déjà attribué. Bien que possible, cette pratique complexifie la configuration.

La surcharge de beans sera dépréciée dans une prochaine version.

Pour désactiver complètement la surcharge de beans, vous pouvez définir l'indicateur `allowBeanDefinitionOverriding` sur `false` dans l'`ApplicationContext` avant son actualisation. Dans ce cas, une exception est levée si une surcharge de bean est utilisée.

Par défaut, le conteneur consigne chaque tentative de surcharge d'un bean au niveau INFO afin que vous puissiez adapter votre configuration en conséquence. Bien que cela soit déconseillé, vous pouvez désactiver ces journaux en définissant l'indicateur `allowBeanDefinitionOverriding` sur `true`.

Configuration Java

Si vous utilisez la configuration Java, une méthode `@Bean` correspondante surcharge toujours silencieusement une classe de bean analysée portant le même nom de composante, à condition que le type de retour de la méthode `@Bean` corresponde à cette classe de bean. Cela signifie simplement que le conteneur appellera la méthode de fabrique `@Bean` de préférence à tout constructeur pré-déclaré sur la classe de bean.

Les concepteurs de Spring savent qu'il est pratique de surcharger les beans dans les scénarios de test, et cette fonctionnalité est explicitement prise en charge.

Nommage des beans

Chaque bean possède un ou plusieurs identifiants. Ces identifiants doivent être uniques au sein du conteneur qui l'héberge. Un bean ne possède généralement qu'un seul identifiant. Toutefois, s'il en requiert plusieurs, les identifiants supplémentaires peuvent être considérés comme des alias.

Dans les métadonnées de configuration XML, vous utilisez l'attribut `id`, l'attribut `name`, ou les deux, pour spécifier les identifiants des beans. L'attribut `id` permet de spécifier un seul identifiant. Par convention, ces noms sont alphanumériques («myBean», «someService»,...), mais ils peuvent également contenir des caractères spéciaux. Si vous souhaitez ajouter d'autres alias au bean, vous pouvez les spécifier dans l'attribut `name`, séparés par une virgule (,), un point-virgule (;) ou un espace. Bien que l'attribut `id` soit défini comme un type `xsd:string`, l'unicité des identifiants de beans est assurée par le conteneur, et non par les analyseurs XML.

Il n'est pas obligatoire de fournir un nom ou un identifiant pour un bean. Si vous ne spécifiez pas explicitement de nom ou d'identifiant, le conteneur génère un nom unique pour ce bean. Toutefois, si vous souhaitez y faire référence par son nom, via l'élément `ref` ou une recherche de type Service Locator, vous devez impérativement le nommer. L'absence de nom est généralement justifiée par l'utilisation de beans internes et l'injection automatique de dépendances.

Conventions de nommage des beans

La convention consiste à utiliser la convention standard Java pour les noms de champs d'instance lors du nommage des beans. Autrement dit, les noms de beans commencent par une minuscule et sont ensuite encodés en camelCase. Par exemple : accountManager, accountService, userDao, loginController,...

Nommer les beans de manière cohérente facilite la lecture et la compréhension de votre configuration. De plus, si vous utilisez Spring AOP, cela simplifie grandement l'application de conseils à un ensemble de beans liés par leur nom.

Lors de l'analyse des composantes dans le classpath, Spring génère des noms de beans pour les composantes sans nom, en suivant les règles décrites précédemment : il s'agit essentiellement de convertir le premier caractère du nom de classe en minuscule. Toutefois, dans le cas particulier (rare) où le nom comporte plusieurs caractères et que les deux premiers sont en majuscules, la casse d'origine est conservée. Ces règles sont identiques à celles définies par `java.beans.Introspector.decapitalize` (utilisée ici par Spring).

Alias d'un bean en dehors de sa définition

Dans la définition d'un bean, vous pouvez lui attribuer plusieurs noms. Pour cela, combinez un nom spécifié par l'attribut `id` avec un ou plusieurs autres noms dans l'attribut `name`. Ces noms peuvent être des alias équivalents pour un même bean et s'avèrent utiles dans certains cas, par exemple pour permettre à chaque composante d'une application de faire référence à une dépendance commune grâce à un nom de bean spécifique à cette composante.

Cependant, spécifier tous les alias à l'endroit où le bean est défini n'est pas toujours suffisant. Il est parfois souhaitable d'introduire un alias pour un bean défini ailleurs. C'est notamment le cas dans les grands systèmes où la configuration est répartie entre les sous-systèmes, chacun possédant son propre ensemble de définitions d'objets. Dans les métadonnées de configuration XML, vous pouvez utiliser l'élément `<alias/>` à cette fin. L'exemple suivant illustre la procédure :

<alias name="fromName" alias="toName"/>

Dans ce cas, un bean (dans le même conteneur) nommé fromName peut également, après l'utilisation de cette définition d'alias, être désigné par toName.

Par exemple, les métadonnées de configuration du sous-système A peuvent faire référence à une DataSource nommée subsystemA-dataSource. Les métadonnées de configuration du sous-système B peuvent faire référence à une DataSource nommée subsystemB-dataSource. Lors de la composition de l'application principale utilisant ces deux sous-systèmes, cette dernière fait référence à la DataSource nommée myApp-dataSource. Pour que ces trois noms désignent le même objet, vous pouvez ajouter les définitions d'alias suivantes aux métadonnées de configuration :

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

Désormais, chaque composant et l'application principale peuvent accéder à la source de données via un nom unique, garantissant l'absence de conflit avec toute autre définition (créant ainsi un espace de noms), tout en faisant référence au même bean.

Instanciation de beans

Une définition de bean est en quelque sorte une recette pour créer un ou plusieurs objets. Le conteneur consulte la recette d'un bean nommé lorsqu'il le demande et utilise les métadonnées de configuration encapsulées par cette définition pour créer (ou acquérir) un objet concret.

Si vous utilisez des métadonnées de configuration XML, vous spécifiez le type (ou la classe) de l'objet à instancier dans l'attribut `class` de l'élément `<bean/>`. Cet attribut `class` (qui, en interne, est une propriété `Class` d'une instance de `BeanDefinition`) est généralement obligatoire. Vous pouvez utiliser la propriété `Class` de deux manières :

Noms de classes imbriquées

Pour configurer une définition de bean pour une classe imbriquée, vous pouvez utiliser soit le nom binaire, soit le nom source de la classe imbriquée.

Par exemple, si vous avez une classe nommée SomeThing dans le paquet com.example, et que cette classe SomeThing possède une classe imbriquée statique nommée OtherThing, vous pouvez les séparer par un signe dollar ($) ou un point (.). La valeur de l'attribut class dans une définition de bean sera alors com.example.SomeThing$OtherThing ou com.example.SomeThing.OtherThing.

Instanciation avec un constructeur

Lorsque vous créez un bean à l'aide d'un constructeur, toutes les classes standard sont compatibles avec Spring. Autrement dit, la classe développée n'a pas besoin d'implémenter d'interfaces spécifiques ni d'être codée d'une manière particulière. Il suffit de spécifier la classe du bean. Cependant, selon le type d'IoC utilisé pour ce bean, un constructeur par défaut (vide) peut être nécessaire.

Le conteneur IoC de Spring peut gérer pratiquement n'importe quelle classe. Il ne se limite pas aux JavaBeans. La plupart des utilisateurs de Spring préfèrent les JavaBeans avec un constructeur par défaut (sans argument) et des accesseurs et mutateurs appropriés, calqués sur les propriétés du conteneur. Vous pouvez également inclure des classes plus spécifiques, non conformes au style JavaBean, dans votre conteneur. Par exemple, si vous devez utiliser un pool de connexions hérité ne respectant pas la spécification JavaBean, Spring peut également le gérer.

Grâce aux métadonnées de configuration XML, vous pouvez spécifier votre classe de bean comme suit :

<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>     

Concernant les arguments du constructeur, le conteneur peut sélectionner le constructeur correspondant parmi plusieurs constructeurs surchargés. Toutefois, afin d'éviter toute ambiguïté, il est recommandé de rédiger des signatures de constructeur aussi simples que possible.

Instanciation avec une méthode de fabrique statique

Lors de la définition d'un bean créé à l'aide d'une méthode de fabrique statique, utilisez l'attribut `class` pour spécifier la classe contenant cette méthode et l'attribut `factory-method` pour indiquer son nom. Vous devriez pouvoir appeler cette méthode (avec des arguments optionnels, comme expliqué plus loin) et obtenir un objet instancié, qui sera ensuite traité comme s'il avait été créé par un constructeur. Ce type de définition de bean permet notamment d'appeler des fabriques statiques dans du code existant.

La définition de bean suivante indique que le bean sera créé par l'appel d'une méthode de fabrique. Elle ne spécifie pas le type (classe) de l'objet retourné, mais la classe contenant la méthode de fabrique. Dans cet exemple, la méthode `createInstance()` doit être statique. L'exemple suivant illustre la spécification d'une méthode de fabrique :

<bean id="clientService"
     class="examples.ClientService"
     factory-method="createInstance"/>

L'exemple Java suivant illustre une classe compatible avec la définition de bean précédente :

  1. public class ClientService {
  2.      private static ClientService clientService = new ClientService();
  3.      private ClientService() {}
  4.  
  5.      public static ClientService createInstance() {
  6.           return clientService;
  7.      }
  8. }

ou l'exemple Kotlin suivant :

  1. class ClientService private constructor() {
  2.     companion object {
  3.         private val clientService = ClientService()
  4.         @JvmStatic
  5.         fun createInstance() = clientService
  6.     }
  7. }    

Dans le cas des arguments de méthode de fabrique, le conteneur peut sélectionner la méthode correspondante parmi plusieurs méthodes surchargées portant le même nom. Cela dit, afin d'éviter toute ambiguïté, il est recommandé de concevoir des signatures de méthodes de fabrique aussi simples que possible.

Un cas typique de surcharge de méthodes de fabrique problématique est celui de Mockito, avec ses nombreuses surcharges de la méthode `mock`. Choisissez la variante de `mock` la plus spécifique possible :

<bean id="clientService" class="org.mockito.Mockito" factory-method="mock">
     <constructor-arg type="java.lang.Class" value="examples.ClientService"/>
     <constructor-arg type="java.lang.String" value="clientService"/>
</bean>     

Instanciation via une méthode de fabrique d'instances

De manière similaire à l'instanciation via une méthode de fabrique statique, l'instanciation via une méthode de fabrique d'instances appelle une méthode non statique d'un bean existant du conteneur pour créer un nouveau bean. Pour utiliser ce mécanisme, laissez l'attribut `class` vide et, dans l'attribut `factory-bean`, spécifiez le nom d'un bean du conteneur actuel (ou parent ou ancêtre) contenant la méthode d'instance à appeler pour créer l'objet. Définissez le nom de la méthode de fabrique elle-même avec l'attribut `factory-method`. L'exemple suivant illustre la configuration d'un tel bean :

<!-- le bean de fabriquequi contient une méthode appelée createClientServiceInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
     <!-- injectez toutes les dépendances requises par ce bean de localisation. -->
</bean>

<!-- le bean à créer via le bean d'usine -->
<bean id="clientService"
     factory-bean="serviceLocator"
     factory-method="createClientServiceInstance"/>

L'exemple Java suivant illustre la classe correspondante :

  1. public class DefaultServiceLocator {
  2.  
  3.      private static ClientService clientService = new ClientServiceImpl();
  4.  
  5.      public ClientService createClientServiceInstance() {
  6.           return clientService;
  7.      }
  8. }

ou l'exemple Kotlin suivant :

  1. class DefaultServiceLocator {
  2.     companion object {
  3.         private val clientService = ClientServiceImpl()
  4.     }
  5.     fun createClientServiceInstance(): ClientService {
  6.         return clientService
  7.     }
  8. }

Une classe de fabrique peut également contenir plusieurs méthodes de fabrique, comme le montre l'exemple suivant :

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
     <!-- injectez toutes les dépendances requises par ce bean de localisation. -->
</bean>

<bean id="clientService"
     factory-bean="serviceLocator"
     factory-method="createClientServiceInstance"/>

<bean id="accountService"
     factory-bean="serviceLocator"
     factory-method="createAccountServiceInstance"/>     

L'exemple Java suivant illustre la classe correspondante :

  1. public class DefaultServiceLocator {
  2.  
  3.      private static ClientService clientService = new ClientServiceImpl();
  4.  
  5.      private static AccountService accountService = new AccountServiceImpl();
  6.  
  7.      public ClientService createClientServiceInstance() {
  8.           return clientService;
  9.      }
  10.  
  11.      public AccountService createAccountServiceInstance() {
  12.           return accountService;
  13.      }
  14. }     

ou l'exemple Kotlin suivant :

  1. class DefaultServiceLocator {
  2.     companion object {
  3.         private val clientService = ClientServiceImpl()
  4.         private val accountService = AccountServiceImpl()
  5.     }
  6.  
  7.     fun createClientServiceInstance(): ClientService {
  8.         return clientService
  9.     }
  10.  
  11.     fun createAccountServiceInstance(): AccountService {
  12.         return accountService
  13.     }
  14. }

Dans la documentation Spring, le terme «factory bean» désigne un bean configuré dans le conteneur Spring et créant des objets via une méthode de fabrique d'instance ou statique. En revanche, « FactoryBean » (notez la majuscule) désigne une classe d'implémentation FactoryBean spécifique à Spring.

Détermination du type d'exécution d'un bean

Déterminer le type d'exécution d'un bean spécifique n'est pas chose aisée. Une classe spécifiée dans la définition des métadonnées du bean n'est qu'une référence initiale, potentiellement associée à une méthode de fabrique déclarée ou appartenant à la classe FactoryBean. Dans ce dernier cas, le type d'exécution du bean peut différer, voire ne pas être défini du tout, notamment pour une méthode de fabrique au niveau de l'instance (qui est alors résolue par le nom du bean de fabrique spécifié). De plus, le proxy AOP peut encapsuler une instance de bean dans un proxy basé sur une interface, limitant ainsi l'exposition du type réel du bean cible (à l'exception de ses interfaces implémentées).

La méthode recommandée pour connaître le type d'exécution réel d'un bean particulier consiste à appeler `BeanFactory.getType` sur le nom du bean spécifié. Cette méthode prend en compte tous les cas précédents et renvoie le type d'objet qu'un appel à `BeanFactory.getBean` renverra pour ce même nom de bean.



Dernière mise à jour : Lundi, le 16 février 2026