Spring Boot et Spring Cloud

Spring Boot expliqué : starters, auto-configuration et votre première application

Débutant10 min de lectureMis à jour

Cette page a été traduite de l'anglais avec l'aide de l'IA, puis vérifiée par rapport à l'original. Lire la version anglaise

À l'issue de ce guide, vous saurez ce que Spring Boot apporte au-dessus de Spring Framework, comment un starter et une classe d'auto-configuration travaillent ensemble, comment lire le rapport d'évaluation des conditions quand un bean manque, et comment écrire votre première application Boot 3 sans considérer le framework comme de la magie.

Ce que vous allez apprendre

  • Expliquer ce que Spring Boot ajoute à Spring Framework et ce qu'il ne change pas
  • Décrire comment starters, classes d'auto-configuration et conditions produisent des beans au démarrage
  • Créer et exécuter une application Spring Boot 3 avec Maven et comprendre son point d'entrée
  • Lire le rapport d'évaluation des conditions pour savoir pourquoi une auto-configuration s'est appliquée ou non
  • Surcharger ou exclure une auto-configuration sans risque en déclarant votre propre bean

Aujourd'hui, la plupart des services Java sont écrits avec Spring Boot, et ce n'est pas un nouveau modèle de programmation qui l'explique. Boot conserve Spring Framework tel quel et supprime ce qui occupait autrefois la première semaine d'un projet : choisir des versions de bibliothèques compatibles, câbler les beans d'infrastructure, configurer un conteneur de servlets et rédiger le descripteur de déploiement. Vous ajoutez une dépendance, Boot la détecte, et une configuration raisonnable apparaît. Cette page explique la mécanique qui se cache derrière cette expérience, pour que vous puissiez prévoir ce que Boot va faire, comprendre pourquoi il a fait autre chose et reprendre la main quand son avis n'est pas le vôtre. Tout ce qui suit vise Spring Boot 3 sur Java 17 ou plus récent.

Ce que Boot ajoute et ce qu'il laisse intact

Spring Framework vous fournit un conteneur d'inversion de contrôle, la programmation orientée aspect, la gestion des transactions, Spring MVC et une longue liste d'intégrations. Ce qu'il s'abstient volontairement de vous donner, c'est une opinion sur la façon de les assembler. Une application Spring classique déclarait donc à la main, en XML ou en configuration Java, une DataSource, une EntityManagerFactory, un gestionnaire de transactions, une DispatcherServlet, des convertisseurs de messages et un résolveur de vues, puis empaquetait le tout dans un WAR destiné à un serveur d'applications externe.

Spring Boot apporte quatre choses sur ces fondations.

La gestion des dépendances. Le BOM (bill of materials) spring-boot-dependencies fixe la version de plusieurs centaines de bibliothèques dont on sait qu'elles fonctionnent ensemble. Vous déclarez spring-boot-starter-web sans version et héritez d'un ensemble cohérent.

Les starters. Un starter est un artefact Maven ou Gradle vide dont le seul rôle est de tirer un groupe choisi de dépendances pour une fonctionnalité donnée : web, données, sécurité, validation, tests.

L'auto-configuration. Un ensemble de classes @Configuration qui n'enregistrent des beans d'infrastructure que lorsque les bonnes conditions sont réunies : la bibliothèque est dans le classpath, aucun bean utilisateur de ce type n'existe, une propriété est définie.

Les fonctionnalités de production. Un serveur servlet ou réactif embarqué pour que l'application se lance avec java -jar, une configuration externalisée, et le module Actuator pour les endpoints de santé, de métriques et d'administration.

Rien dans cette liste ne change la manière d'écrire un service, un repository ou un contrôleur. Si vous savez lire une application Spring classique, vous savez lire une application Boot ; la différence tient à la quantité de configuration d'infrastructure visible dans les sources.

Une première application

Générez un projet avec Spring Initializr (le site web, la CLI spring ou l'assistant intégré à IntelliJ IDEA, Eclipse et VS Code) en cochant la dépendance Web. Côté Maven, vous obtenez un pom.xml dont le parent est spring-boot-starter-parent :

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.5.5</version>
</parent>

<properties>
  <java.version>21</java.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

Le POM parent fournit le BOM, des réglages raisonnables pour le compilateur et le filtrage des ressources, ainsi que le plugin qui reconditionne le build en jar exécutable. Si votre organisation dispose déjà d'un POM parent d'entreprise, importez plutôt spring-boot-dependencies dans dependencyManagement avec <scope>import</scope> : vous perdez les réglages par défaut du plugin mais conservez l'alignement des versions.

Le point d'entrée tient en une seule classe :

package com.example.shop;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class ShopApplication {

    public static void main(String[] args) {
        SpringApplication.run(ShopApplication.class, args);
    }
}

@RestController
class HelloController {

    @GetMapping("/hello")
    String hello() {
        return "Hello from Spring Boot";
    }
}

Lancez ./mvnw spring-boot:run (ou java -jar target/shop-0.0.1-SNAPSHOT.jar après ./mvnw package) et Tomcat répond sur le port 8080. Vous n'avez déclaré ni le conteneur de servlets, ni la DispatcherServlet, ni le convertisseur JSON de Jackson, ni la page d'erreur : l'auto-configuration s'en est chargée, parce que spring-boot-starter-web a placé les classes nécessaires dans le classpath.

@SpringBootApplication combine trois annotations :

AnnotationEffet
@SpringBootConfigurationMarque la classe comme @Configuration et permet aux tests de la trouver automatiquement
@EnableAutoConfigurationActive le mécanisme d'auto-configuration décrit plus bas
@ComponentScanParcourt le package de cette classe et ses sous-packages à la recherche de @Component, @Service, @Repository, @Controller et @Configuration

Le détail du scan des composants a une conséquence pratique : placez la classe d'application dans votre package racine (com.example.shop), ni dans le package par défaut, ni dans un sous-package comme com.example.shop.config. Les classes situées hors de l'arborescence scannée sont ignorées sans le moindre message, et c'est l'une des surprises les plus courantes, du type « mon bean est introuvable ».

Les starters : des lots de dépendances avec une convention de nommage

Ouvrez spring-boot-starter-web dans la vue des dépendances de votre IDE : vous n'y trouverez aucun code, seulement des dépendances transitives, à savoir spring-boot-starter (le cœur, les logs et le support YAML), spring-boot-starter-json (Jackson), spring-boot-starter-tomcat et les modules du framework spring-web/spring-webmvc. Les starters existent pour que « je veux construire une application web » tienne en une ligne au lieu de huit, et pour que changer de technologie se résume à des exclusions : excluez spring-boot-starter-tomcat, ajoutez spring-boot-starter-jetty, et le serveur embarqué change sans modifier une ligne de code.

Les starters officiels s'appellent spring-boot-starter-*. La documentation de référence demande aux tiers d'utiliser le motif inverse, acme-spring-boot-starter, afin que le préfixe spring-boot-starter- reste réservé à l'équipe Spring. Cette convention vous indique d'un coup d'œil si un starter est maintenu par le projet ou par la communauté.

Quelques starters à connaître rapidement :

StarterCe qu'il apporte
spring-boot-starter-webSpring MVC, Jackson, Tomcat embarqué
spring-boot-starter-webfluxStack web réactive sur Reactor Netty
spring-boot-starter-data-jpaSpring Data JPA, Hibernate, pool de connexions HikariCP
spring-boot-starter-validationJakarta Bean Validation avec Hibernate Validator
spring-boot-starter-securitySpring Security avec une configuration sécurisée par défaut
spring-boot-starter-actuatorEndpoints de santé, de métriques et d'administration
spring-boot-starter-testJUnit 5, AssertJ, Mockito, Spring Test, assertions JSON

Notez que spring-boot-starter-validation est distinct de -web. Cette séparation date de Boot 2.3 ; si @Valid semble sans effet dans un projet tout neuf, c'est généralement ce starter qui manque.

Comment fonctionne l'auto-configuration

L'auto-configuration, c'est de la configuration Spring en Java tout à fait ordinaire, avec deux ajouts : un fichier d'enregistrement qui liste les classes candidates, et des conditions qui décident si chacune s'applique.

L'enregistrement

Depuis Boot 2.7, et exclusivement depuis la 3.0, les classes d'auto-configuration sont listées, un nom pleinement qualifié par ligne, dans une ressource nommée META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports à l'intérieur du jar qui les fournit. L'ancienne clé de spring.factories dédiée à l'auto-configuration a été supprimée en 3.0 ; une bibliothèque qui en dépend encore ne sera pas prise en compte. @EnableAutoConfiguration lit tous les fichiers de ce type présents dans le classpath et importe les classes qu'ils référencent. Le seul jar spring-boot-autoconfigure en contient largement plus d'une centaine.

Les conditions

Chaque classe, et souvent chaque méthode @Bean qu'elle contient, porte des conditions issues du package org.springframework.boot.autoconfigure.condition :

ConditionS'applique quand
@ConditionalOnClass / @ConditionalOnMissingClassUne classe est (ou n'est pas) dans le classpath
@ConditionalOnBean / @ConditionalOnMissingBeanUn bean d'un type ou d'un nom donné est (ou n'est pas) déjà défini
@ConditionalOnPropertyUne propriété a une valeur donnée, ou est absente avec matchIfMissing
@ConditionalOnWebApplication / @ConditionalOnNotWebApplicationL'application est (ou n'est pas) une application web servlet ou réactive
@ConditionalOnResourceUne ressource, par exemple un fichier, existe dans le classpath
@ConditionalOnExpressionUne expression SpEL s'évalue à true

C'est @ConditionalOnMissingBean qui rend Boot courtois. Les classes d'auto-configuration sont traitées après vos propres classes @Configuration ; dès que vous déclarez un bean d'un type que Boot aurait créé, la version de Boot s'efface. Voilà pourquoi le conseil habituel pour personnaliser Boot est « définissez le bean vous-même » plutôt que « désactivez le bean de Boot ».

Voici une version simplifiée de l'auto-configuration de la source de données, écrite comme vous écririez la vôtre :

@AutoConfiguration
@ConditionalOnClass(DataSource.class)          // only if JDBC support is present
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean                  // back off if the app declared one
    @ConditionalOnProperty(prefix = "spring.datasource", name = "url")
    DataSource dataSource(DataSourceProperties properties) {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl(properties.getUrl());
        ds.setUsername(properties.getUsername());
        ds.setPassword(properties.getPassword());
        return ds;
    }
}

@AutoConfiguration (introduite en 2.7) remplace @Configuration pour ces classes. Elle désactive le proxying des méthodes @Bean, dont l'auto-configuration n'a pas besoin, et accepte des attributs before/after pour s'ordonner par rapport aux autres auto-configurations. L'ordre compte, car une condition comme @ConditionalOnBean n'a de sens que si le bean qu'elle recherche a eu l'occasion d'être enregistré avant.

Écrire la vôtre

Les équipes qui maintiennent plusieurs services finissent généralement par créer une bibliothèque partagée qui configure partout de la même façon les aspects communs à tous les services : un ObjectMapper ajusté, un client HTTP qui propage les en-têtes de tracing, un gestionnaire d'erreurs standard. Livrer cela sous forme d'auto-configuration plutôt que de classe de base laisse aux services consommateurs la liberté d'en surcharger chaque élément :

acme-http-spring-boot-starter/
└── src/main/resources/META-INF/spring/
    └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
        (contains: com.acme.http.AcmeRestClientAutoConfiguration)
@AutoConfiguration
@ConditionalOnClass(RestClient.class)
public class AcmeRestClientAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    RestClient.Builder acmeRestClientBuilder() {
        return RestClient.builder()
                .defaultHeader("X-Acme-Caller", "shop");
    }
}

Gardez les classes d'auto-configuration hors du périmètre de @ComponentScan des applications qui les utilisent : si un scan les récupère comme de simples classes @Configuration, la garantie d'ordre disparaît et @ConditionalOnMissingBean peut se comporter de façon inattendue. Il suffit de placer la bibliothèque dans sa propre hiérarchie de packages (com.acme.http, pas com.example.shop.*).

Comprendre ce qui s'est passé

L'auto-configuration est déterministe, mais pas toujours évidente, et Boot vous offre deux moyens de voir ses décisions.

Démarrez l'application avec --debug (ou debug=true dans application.properties) et lisez le rapport d'évaluation des conditions dans les logs. Il comporte trois parties : les correspondances positives (les auto-configurations appliquées, avec la condition qui les a laissées passer), les correspondances négatives (celles qui ne l'ont pas été, avec la condition qui a échoué) et les exclusions. Une entrée négative typique ressemble à ceci :

DataSourceAutoConfiguration:
   Did not match:
      - @ConditionalOnClass did not find required class 'javax.sql.DataSource'

Cette ligne vous dit exactement quelle dépendance ajouter. Quand un bean manque sans raison apparente, ce rapport répond à la question plus vite que la lecture du code source.

Si le starter Actuator est présent, la même information est disponible à l'exécution via l'endpoint conditions une fois que vous l'exposez, ce qui est pratique dans les environnements où vous ne pouvez pas redémarrer avec un flag.

Surcharger et exclure

Il existe trois niveaux de contrôle, du plus doux au plus brutal.

  1. Définir une propriété. La plupart des auto-configurations sont pilotées par des classes @ConfigurationProperties ; server.port, spring.datasource.url ou spring.jackson.default-property-inclusion modifient donc le comportement sans toucher au code. La liste complète figure dans l'annexe « Common Application Properties » de la documentation de référence.
  2. Déclarer le bean vous-même. Grâce à @ConditionalOnMissingBean, votre bean remplace celui de Boot. Si vous voulez le bean de Boot avec un ajustement, cherchez un point d'extension de type customizer : WebMvcConfigurer, Jackson2ObjectMapperBuilderCustomizer, RestClientCustomizer, TomcatConnectorCustomizer et bien d'autres existent précisément pour ajuster sans remplacer.
  3. Exclure l'auto-configuration. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) ou la propriété spring.autoconfigure.exclude la retire complètement. Réservez cette option au cas où la bibliothèque est dans le classpath pour une raison sans rapport avec la fonctionnalité que Boot en déduit, par exemple un driver JDBC utilisé uniquement par un outil au moment du build.

Recourez rarement au niveau 3. Une auto-configuration exclue a souvent des dépendants (DataSourceAutoConfiguration alimente JPA, les templates JDBC, Flyway, les indicateurs de santé), et en exclure une peut déclencher une cascade de beans manquants plus difficile à diagnostiquer que le problème de départ.

Erreurs courantes

Une classe d'application dans le mauvais package. Les composants situés dans un package frère ou parent ne sont jamais scannés. Déplacez la classe dans le package racine ou ajoutez scanBasePackages à @SpringBootApplication ; la première solution est plus propre.

Lutter contre le POM parent. Surcharger une version gérée (par exemple imposer une version plus récente d'Hibernate) est légitime, mais doit passer par la propriété exposée par le BOM (<hibernate.version>) et non par une version déclarée sur la dépendance, afin que l'alignement des dépendances transitives soit préservé. Lisez d'abord les notes de version de la bibliothèque : la version du BOM est celle avec laquelle l'auto-configuration de Boot a été testée.

Croire qu'un starter équivaut à une fonctionnalité. Ajouter spring-boot-starter-data-jpa sans driver de base de données ni spring.datasource.url fait échouer le démarrage avec un message évoquant une base de données embarquée. Boot ne peut configurer que ce que le classpath et les propriétés décrivent.

Mélanger javax.* et jakarta.*. Une application Boot 3 dont une dépendance utilise encore javax.servlet ou javax.persistence compile, mais échoue à l'exécution avec NoClassDefFoundError ou des entités non mappées. Vérifiez que chaque bibliothèque tierce existe en version compatible Jakarta avant de migrer.

Prendre --debug pour un niveau de log. Le flag active le rapport des conditions et les logs DEBUG de quelques loggers du cœur, pas ceux de vos propres packages ; pour cela, utilisez logging.level.com.example=DEBUG.

Points clés à retenir

  • Spring Boot, c'est Spring Framework plus la gestion des dépendances, les starters, l'auto-configuration et des fonctionnalités de production ; le modèle de programmation ne change pas.
  • Un starter est un lot de dépendances ; une auto-configuration est une classe @Configuration conditionnelle, enregistrée dans META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
  • Des conditions comme @ConditionalOnClass et @ConditionalOnMissingBean font s'effacer les beans de Boot dès que vous déclarez les vôtres, ce qui est la manière prévue de le personnaliser.
  • @SpringBootApplication combine configuration, auto-configuration et scan des composants à partir du package de la classe, d'où l'importance de son emplacement.
  • Quand un bean manque ou apparaît de façon inattendue, le rapport d'évaluation des conditions (--debug, ou l'endpoint Actuator conditions) vous indique quelle condition a tranché.
  • Préférez les propriétés et les beans customizer à l'exclusion d'auto-configurations : les exclusions ont des effets en cascade.

Sources et lectures complémentaires

Questions fréquentes

Spring Boot est-il un framework différent de Spring ?
Non. Spring Boot est une couche posée sur Spring Framework. Chaque bean, annotation et abstraction que vous utilisez dans une application Boot (injection de dépendances, @Transactional, Spring MVC, Spring Data) vient du framework et des projets de son écosystème. Boot ajoute une gestion des dépendances aux choix assumés, l'auto-configuration, un serveur embarqué et des fonctionnalités de production comme Actuator.
Pourquoi Spring Boot 3 exige-t-il Java 17 et les packages jakarta.* ?
Spring Boot 3 repose sur Spring Framework 6, qui a fixé Java 17 comme version minimale et est passé de Java EE à Jakarta EE 9+. Jakarta EE 9 a renommé tous les packages d'API javax.* en jakarta.*, si bien que les imports liés aux servlets, à la persistance, à la validation et à d'autres API changent. Les bibliothèques qui livrent encore des classes javax.* ne fonctionneront pas avec Boot 3 tant qu'elles n'auront pas été mises à jour.
Comment savoir quelles auto-configurations ont été appliquées à mon application ?
Démarrez l'application avec la propriété debug activée (par exemple --debug en ligne de commande ou debug=true dans application.properties). Boot écrit dans les logs un rapport d'évaluation des conditions qui liste les correspondances positives, les correspondances négatives et la condition exacte qui a tranché dans chaque cas. Avec Actuator, vous retrouvez les mêmes informations à l'exécution via l'endpoint conditions.
Si je définis mon propre bean DataSource, Boot en créera-t-il quand même un ?
Non. DataSourceAutoConfiguration est protégée par @ConditionalOnMissingBean, et la configuration de l'utilisateur est traitée avant l'auto-configuration. Votre bean l'emporte et celui de l'auto-configuration est ignoré. Ce retrait automatique est la manière standard de personnaliser Boot : définissez le bean vous-même plutôt que de lutter contre les valeurs par défaut.
L'auto-configuration ralentit-elle le démarrage ?
Chaque classe d'auto-configuration est évaluée au démarrage, mais la plupart sont écartées presque immédiatement par une vérification @ConditionalOnClass peu coûteuse, qui échoue avant même que la classe ne soit chargée. Ce qui pèse vraiment sur le temps de démarrage, c'est le travail des beans effectivement créés (ouverture des pools de connexions, construction des métamodèles Hibernate, démarrage de Tomcat), pas l'évaluation des conditions elle-même.