Spring Boot 4 : breaking changes à connaître avant de migrer
Vous envisagez de migrer vers Spring Boot 4 ? Avant de lancer ./mvnw spring-boot:upgrade, prenez le temps de lire ce qui suit. Cette version majeure introduit des changements incompatibles qui peuvent casser votre application si vous n'êtes pas préparé.
Voici la liste complète des breaking changes à connaître :
- Java 21 minimum requis
- Passage à Jakarta EE 11
- Hibernate 7 et changements JPA
- Nouvelle configuration de sécurité
- Suppression des propriétés dépréciées
- Changements dans les starters
- Modifications des métriques et observabilité
- Évolutions du support cloud natif
Pour une analyse complète sur l'opportunité de migrer, consultez notre article pilier : Spring Boot 4 : faut-il migrer maintenant ?
Java 21 : le minimum requis
Ce qui change
Spring Boot 4 abandonne le support de Java 17. La version minimale est désormais Java 21 LTS.
<!-- pom.xml - Avant (Spring Boot 3.x) -->
<properties>
<java.version>17</java.version>
</properties>
<!-- pom.xml - Après (Spring Boot 4.x) -->
<properties>
<java.version>21</java.version>
</properties>Impact sur votre projet
| Situation | Action requise |
|---|---|
| Déjà en Java 21 | Aucune action |
| Java 17 | Mise à jour JDK + tests de régression |
| Java 11 ou moins | Migration majeure (2 versions) |
Fonctionnalités Java 21 à exploiter
Spring Boot 4 tire parti des nouvelles fonctionnalités :
// Virtual Threads (Project Loom) - natif Spring Boot 4
@Bean
public TomcatProtocolHandlerCustomizer<?> virtualThreadsCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
// Pattern Matching (Java 21)
public String processPayment(Payment payment) {
return switch (payment) {
case CreditCard cc -> processCreditCard(cc);
case BankTransfer bt -> processBankTransfer(bt);
case Crypto crypto -> processCrypto(crypto);
};
}
// Record Patterns
if (response instanceof ApiResponse(var data, var status)
&& status == 200) {
return data;
}Checklist Java 21
- ☐ Vérifier la compatibilité de toutes les dépendances avec Java 21
- ☐ Mettre à jour le JDK sur tous les environnements (dev, CI, prod)
- ☐ Tester les performances (GC, startup time)
- ☐ Mettre à jour les images Docker (
eclipse-temurin:21-jre)
Jakarta EE 11 : le grand saut
Ce qui change
Spring Boot 4 passe de Jakarta EE 10 à Jakarta EE 11. Les impacts principaux :
// Changements de packages - déjà effectués en Spring Boot 3
// javax.* → jakarta.* (rappel)
// NOUVEAU en Jakarta EE 11 : APIs supprimées
// jakarta.activation → standalone
// jakarta.xml.bind → standalone (JAXB)APIs supprimées du namespace Jakarta
| API | Statut | Alternative |
|---|---|---|
| JAXB (XML Binding) | Retiré de Jakarta EE | org.glassfish.jaxb:jaxb-runtime |
| JAX-WS (SOAP) | Retiré | com.sun.xml.ws:jaxws-rt |
| Jakarta Activation | Standalone | jakarta.activation:jakarta.activation-api |
| CommonJ | Supprimé | java.util.concurrent |
Migration des dépendances XML
<!-- Si vous utilisez JAXB (XML) -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
</dependency>
<!-- Si vous utilisez SOAP (rare en 2025) -->
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>4.0.2</version>
</dependency>Servlet 6.1
// Nouvelle API Servlet 6.1
// Changement : HttpServletRequest.getRequestURL()
// Retourne maintenant StringBuilder (non StringBuffer)
// Avant
StringBuffer url = request.getRequestURL();
// Après (compatible)
String url = request.getRequestURL().toString();Hibernate 7 et les changements JPA
Ce qui change
Spring Boot 4 embarque Hibernate 7 avec des breaking changes significatifs.
Suppression des APIs dépréciées
// SUPPRIMÉ : Criteria API legacy
// Avant (Hibernate 5/6)
Criteria criteria = session.createCriteria(User.class);
// Après (Hibernate 7) - Utiliser JPA Criteria
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);Changements de types
// Type UUID natif
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id; // Support natif amélioré
}
// Nouveau : @SoftDelete natif
@Entity
@SoftDelete // Hibernate 7
public class Document {
// deleted_at géré automatiquement
}Configuration Hibernate modifiée
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
query:
mutation_strategy: inline # NouveauLazy Loading par défaut
// ATTENTION : Changement de comportement par défaut
// Hibernate 7 : @ManyToOne est LAZY par défaut (était EAGER)
@Entity
public class Order {
@ManyToOne // Maintenant LAZY par défaut
private Customer customer;
// Si vous vouliez EAGER, expliciter :
@ManyToOne(fetch = FetchType.EAGER)
private Customer customerEager;
}Sécurité : nouvelle configuration obligatoire
Suppression de la configuration dépréciée
// SUPPRIMÉ en Spring Boot 4
// WebSecurityConfigurerAdapter n'existe plus depuis SB 3
// Mais certains patterns dépréciés sont maintenant ERREUR
// AVANT (toléré en SB 3, erreur en SB 4)
http.authorizeRequests()
.antMatchers("/api/**").authenticated();
// APRÈS (obligatoire)
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").authenticated()
);Nouvelles valeurs par défaut
spring:
security:
# NOUVEAU : CSRF activé par défaut même pour APIs REST
# Désactiver explicitement si API stateless
csrf:
enabled: true
# NOUVEAU : Headers de sécurité renforcés
headers:
content-security-policy: "default-src 'self'"
permissions-policy: "geolocation=(), microphone=()"Configuration CORS stricte
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
// NOUVEAU : CORS doit être configuré explicitement
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// CSRF désactivé pour API REST stateless
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/**").authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://app.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
}Propriétés supprimées et renommées
Propriétés supprimées
logging.pattern.file: "%d{yyyy-MM-dd} - %msg%n" # Utiliser logging.pattern.dateformat
spring.datasource.initialization-mode: always # Utiliser spring.sql.init.mode
management.endpoints.jmx.exposure.include: "*" # RenomméPropriétés renommées
| Ancienne propriété | Nouvelle propriété |
|---|---|
spring.datasource.initialization-mode |
spring.sql.init.mode |
spring.jpa.hibernate.use-new-id-generator-mappings |
Supprimée (toujours true) |
server.servlet.encoding.enabled |
server.servlet.encoding.enabled (inchangé mais vérifié) |
management.metrics.tags.* |
management.observations.key-values.* |
Script de détection automatique
grep -r "spring.datasource.initialization-mode" src/
grep -r "use-new-id-generator-mappings" src/
grep -r "management.metrics.tags" src/Changements dans les starters
Starters supprimés
<!-- ❌ SUPPRIMÉS - Ne plus utiliser -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- Remplacé par : configuration manuelle ou Tomcat/Undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- Log4j2 reste supporté mais configuration différente -->Starters modifiés
<!-- spring-boot-starter-web -->
<!-- Tomcat 11 inclus par défaut -->
<!-- Virtual Threads activables via propriété -->
<!-- spring-boot-starter-data-jpa -->
<!-- Hibernate 7 par défaut -->
<!-- HikariCP 6 inclus -->
<!-- spring-boot-starter-security -->
<!-- Spring Security 7 -->
<!-- OAuth2 Authorization Server séparé -->Nouveau starter observabilité
<!-- NOUVEAU : Starter unifié pour observabilité -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-observability</artifactId>
</dependency>
<!-- Inclut : Micrometer, Tracing, Metrics, Logging structuré -->Métriques et observabilité
Migration Micrometer 2.0
// CHANGEMENT : API Micrometer modifiée
// AVANT (Micrometer 1.x)
Metrics.counter("api.requests", "endpoint", "/users").increment();
// APRÈS (Micrometer 2.x)
Metrics.counter("api.requests", Tags.of("endpoint", "/users")).increment();
// NOUVEAU : Observations API (recommandé)
@Observed(name = "user.service", contextualName = "get-user")
public User getUser(Long id) {
return userRepository.findById(id).orElseThrow();
}Nouvelles propriétés
management:
observations:
key-values:
application: my-app
environment: production
tracing:
sampling:
probability: 1.0 # 100% en dev, réduire en prod
otlp:
tracing:
endpoint: http://jaeger:4318/v1/traces
metrics:
endpoint: http://prometheus:4318/v1/metricsLogging structuré par défaut
logging:
structured:
format:
console: ecs # Elastic Common Schema
file: json
# Corrélation de traces automatique
pattern:
correlation: "[${spring.application.name:},%X{traceId:-},%X{spanId:-}]"Cloud natif : GraalVM et containers
Native Image amélioré
<!-- pom.xml - Configuration native simplifiée -->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<!-- Version gérée par Spring Boot 4 -->
<configuration>
<!-- NOUVEAU : Options simplifiées -->
<buildArgs>
<buildArg>--initialize-at-build-time=org.slf4j</buildArg>
</buildArgs>
</configuration>
</plugin>Buildpacks mis à jour
./mvnw spring-boot:build-image \
-Dspring-boot.build-image.imageName=myapp:latestCDS (Class Data Sharing)
FROM eclipse-temurin:21-jre as builder
WORKDIR /app
COPY target/*.jar app.jar
RUN java -Djarmode=tools -jar app.jar extract --layers --destination extracted
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder /app/extracted/dependencies/ ./
COPY --from=builder /app/extracted/spring-boot-loader/ ./
COPY --from=builder /app/extracted/snapshot-dependencies/ ./
COPY --from=builder /app/extracted/application/ ./
RUN java -XX:ArchiveClassesAtExit=app-cds.jsa -Dspring.context.exit=onRefresh -jar app.jar || true
ENTRYPOINT ["java", "-XX:SharedArchiveFile=app-cds.jsa", "-jar", "app.jar"]Checklist de migration complète
Avant de migrer vers Spring Boot 4, validez chaque point :
Prérequis
- ☐ Java 21 installé sur tous les environnements
- ☐ Toutes les dépendances compatibles Java 21
- ☐ Tests de régression à jour
- ☐ Backup de la configuration actuelle
Code
- ☐ Remplacer
authorizeRequests()parauthorizeHttpRequests() - ☐ Vérifier les
@ManyToOne(maintenant LAZY par défaut) - ☐ Migrer les Criteria Hibernate legacy vers JPA Criteria
- ☐ Mettre à jour les appels Micrometer
Configuration
- ☐ Renommer les propriétés dépréciées
- ☐ Configurer CORS explicitement
- ☐ Vérifier les paramètres de sécurité
- ☐ Mettre à jour les profils de logging
Infrastructure
- ☐ Mettre à jour les images Docker (Java 21)
- ☐ Tester le native image si utilisé
- ☐ Valider les dashboards de monitoring
- ☐ Mettre à jour les pipelines CI/CD
Tests
- ☐ Exécuter tous les tests unitaires
- ☐ Exécuter les tests d'intégration
- ☐ Tests de performance (startup, mémoire)
- ☐ Tests de charge en environnement de staging
Ressources officielles
Pour aller plus loin :
Cette liste de breaking changes vous permet d'anticiper les problèmes avant qu'ils ne surviennent en production. Pour une analyse plus stratégique sur l'opportunité de migrer maintenant ou d'attendre, consultez Spring Boot 4 : faut-il migrer maintenant ?