Java 25 LTS est sorti depuis septembre 2025, Spring Boot 3.4 le supporte officiellement depuis décembre 2025 — et pourtant, en mars 2026, la majorité des applications Spring Boot en production tournent encore sur Java 17 ou 21. Les raisons sont compréhensibles : les migrations LTS sont perçues comme risquées, chronophages et peu prioritaires quand l'application fonctionne. Ce guide propose une approche structurée pour démystifier la migration, en couvrant les incompatibilités réelles, les gains mesurables et une checklist opérationnelle.
Pourquoi migrer maintenant plutôt qu'attendre
La question "pourquoi migrer ?" mérite une réponse honnête basée sur des faits.
Support et sécurité : Java 17 entre en fin de support étendu chez la plupart des distributeurs (Azul, Amazon Corretto, Eclipse Temurin) à partir de 2026. Java 21 restera supporté jusqu'à 2028-2029. Java 25 ouvre un horizon de support jusqu'à 2033+. Si vous êtes sur Java 17, la migration vers 25 s'impose dans les 12-18 prochains mois.
Performance concrète : les benchmarks internes de plusieurs équipes utilisant Spring Boot montrent :
- Démarrage applicatif : -15 à -35% avec AOT profiling
- Throughput sous charge : +8 à +12% grâce aux améliorations ZGC
- Consommation mémoire : -10 à -20% en production longue durée
Simplicité de la migration : contrairement à Java 8→11 ou 11→17 qui incluaient des cassures majeures (modules, suppression de APIs dépréciées), la migration 21→25 est remarquablement douce. La majorité des applications Spring Boot 3.x migrent sans modification de code.
Compatibilité Spring Boot avec Java 25
| Version Spring Boot | Support Java 25 | Recommandation |
|--------------------|-----------------|----|
| 3.4.x | ✅ Officiel depuis 12/2025 | Cible recommandée |
| 3.3.x | ✅ Compatible (non certifié) | Migrer vers 3.4 de préférence |
| 3.2.x | ⚠️ Fonctionne, non supporté | Migrer d'abord Spring Boot |
| 3.1.x et avant | ❌ Non compatible | Double migration nécessaire |
Si vous êtes sur Spring Boot 2.x, la migration Java 25 doit être précédée d'une migration Spring Boot 3.x. Ces deux migrations ne se font pas en même temps.
Étape 1 : Analyse des incompatibilités
Avant de toucher au code, l'analyse statique détecte les problèmes potentiels :
# Analyser les dépendances sur des APIs internes JDK
jdeps --jdk-internals --multi-release 25 \
--module-path . \
-cp target/myapp.jar target/myapp.jar
Les problèmes les plus fréquemment rapportés lors de la migration 21→25 :
APIs Sun/internal supprimées : sun.misc.Unsafe continue d'être restreint. Les librairies qui l'utilisent doivent être mises à jour. Vérifier les versions de : Kryo, FST, Netty (< 4.1.90).
Changements de comportement SecurityManager : définitivement supprimé en Java 24, assurez-vous qu'aucune dépendance ne l'instancie.
Sérialisation : le filtre de désérialisation par défaut est plus strict. Des applications qui sérialisaient des objets personnalisés peuvent rencontrer des InvalidClassException.
Étape 2 : Mise à jour du build
Maven
25
25
25
3.4.3
Gradle
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
Dockerfile
FROM eclipse-temurin:25-jre-alpine
# Ou pour GraalVM Native Image
FROM ghcr.io/graalvm/native-image:25
WORKDIR /app
COPY target/myapp.jar app.jar
Étape 3 : Activer les fonctionnalités Java 25 dans Spring Boot
Virtual Threads (Java 21, configuré en Spring Boot 3.4)
Si vous n'avez pas encore activé les Virtual Threads (introduits en Java 21), c'est le moment :
# application.yml
spring:
threads:
virtual:
enabled: true
Avec Spring Boot 3.4 et Java 25, les Virtual Threads sont la configuration recommandée pour les applications web à forte concurrence. Le gain en throughput peut atteindre +300% pour des workloads I/O bound sans modifier le code métier.
Pattern Matching dans les gestionnaires d'événements
@EventListener
public void handleEvent(Object event) {
// Pattern matching finalisé en Java 25 — stable en production
switch (event) {
case UserCreatedEvent(String userId, String email) ->
notificationService.sendWelcome(userId, email);
case OrderPlacedEvent(String orderId, BigDecimal amount) when amount.compareTo(BigDecimal.valueOf(1000)) > 0 ->
fraudDetectionService.flag(orderId);
case OrderPlacedEvent(String orderId, var amount) ->
orderService.process(orderId);
default ->
log.debug("Event non géré: {}", event.getClass().getSimpleName());
}
}
AOT profiling avec Spring Boot
# Générer le profil AOT (à faire en staging avec du trafic représentatif)
java -XX:+AOTMethodProfiling \
-XX:AOTProfilingFile=/profiles/app-$(date +%Y%m%d).aprof \
-jar target/myapp.jar
Étape 4 : Tests de régression et de performance
Tests de régression
# Lancer la suite de tests complète avec Java 25
./mvnw test -Djava.version=25
# Tests d'intégration avec Testcontainers
./mvnw verify -Pintegration-tests
Benchmark de performance avant/après
// Utiliser JMH pour des benchmarks précis
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class ApiThroughputBenchmark {
Comparer les résultats Java 21 vs Java 25 avant de promouvoir en production.
Pièges courants et solutions
Piège 1 : Librairies de sérialisation obsolètes
com.esotericsoftware
kryo
5.6.0
Piège 2 : Tests qui dépendent du comportement du GC
Des tests qui utilisent System.gc() ou qui font des hypothèses sur le timing de collecte peuvent devenir flaky avec ZGC. Refactoriser pour ne pas dépendre du comportement GC.
Piège 3 : Agents Java et instrumentation
Des agents comme New Relic, Datadog ou des agents de test de mutation (PIT) nécessitent souvent des mises à jour pour Java 25. Vérifier la compatibilité avant la migration.
Piège 4 : Oublier de retirer --enable-preview
Des bases de code qui utilisaient le pattern matching en preview avec --enable-preview doivent retirer ce flag pour Java 25, où ces features sont stables. Le laisser ne cause pas d'erreur mais est une mauvaise pratique.
Checklist opérationnelle complète
Analyse préalable
- [ ] Identifier la version Java et Spring Boot actuelle
- [ ] Lancer jdeps --jdk-internals sur le JAR
- [ ] Vérifier la compatibilité des dépendances critiques (Kryo, Netty, agents APM)
- [ ] Lire les release notes Java 25 pour les suppressions d'API
Build
- [ ] Mettre à jour le toolchain Maven/Gradle vers Java 25
- [ ] Mettre à jour Spring Boot vers 3.4.x minimum
- [ ] Retirer les flags --enable-preview obsolètes
- [ ] Mettre à jour le Dockerfile/image de base
Tests
- [ ] Lancer la suite de tests unitaires complète
- [ ] Lancer les tests d'intégration Testcontainers
- [ ] Exécuter les benchmarks JMH comparatifs
- [ ] Valider le démarrage en staging
Performance
- [ ] Activer -XX:+UseZGC -XX:+ZGenerational
- [ ] Activer les Virtual Threads via Spring Boot
- [ ] Générer le profil AOT en staging
- [ ] Mesurer démarrage et throughput vs Java 21
Déploiement
- [ ] Déployer sur 1 instance (canary) avec monitoring
- [ ] Vérifier les métriques JVM (heap, GC pauses, throughput)
- [ ] Rollout progressif sur l'ensemble du parc
Pour les aspects spécifiques à Spring Boot 4, consultez notre guide [Spring Boot 4 : breaking changes](/spring-boot-4-breaking-changes/) et notre comparatif [Spring Boot 3 vs 4](/spring-boot-3-vs-4-comparatif/). Pour tirer parti de GraalVM Native Image avec Java 25, voir notre article [Spring Boot GraalVM Native](/spring-boot-graalvm-native/).