Points Clés
- Estimer le temps et les efforts nécessaires à la mise à niveau vers Java 16 ou 17 peut s'avérer difficile. Cependant, la mise à niveau est souvent plus facile que prévu.
- Cet article et d'autres ressources peuvent aider à résoudre les problèmes courants de mise à niveau de Java.
- Il vous suffit de commencer, de consacrer du temps à la mise à niveau et de déterminer où se situent les problèmes.
- Mettre à niveau étape par étape afin de pouvoir suivre les progrès. Vos coéquipiers et votre direction peuvent également le voir. De cette façon, tout le monde est plus désireux de poursuivre la mise à niveau.
- Diverses fonctionnalités ont été supprimées du JDK, telles que les polices, les certificats, les outils et les méthodes d'API. Si votre application utilise l'une de ces fonctionnalités, vous devez la résoudre lors de la mise à niveau vers une nouvelle version de Java.
- Bien que la mise à niveau de Java puisse être difficile, cela ne prend souvent que quelques heures ou jours. D'après mon expérience, la mise à niveau de Java 11 vers Java 17 est plus facile que la mise à niveau de Java 8 vers Java 11. La mise à niveau des dépendances de votre application résout déjà la plupart, sinon la totalité, de ces problèmes.
À InfoQ Live le 27 avril 2021, j'ai présenté Upgrade to Java 16 or 17 pour expliquer pourquoi vous devriez envisager de mettre à niveau vers Java 16 ou Java 17 (une fois publié), y compris des conseils pratiques sur la façon d'effectuer la mise à niveau.
La session était basée sur mon référentiel GitHub personnel, JavaUpgrades, qui contient de la documentation et des exemples sur les défis courants et les exceptions lors de la mise à niveau vers Java 16 ou Java 17. Vous pouvez utiliser les solutions concrètes, qui sont également incluses, pour vos applications. Les exemples peuvent être exécutés avec Docker et sont construits avec Maven, mais vous pouvez, bien sûr, configurer votre propre version Gradle.
Cet article, ainsi que l'enregistrement de ma session InfoQ, vise à faciliter le processus de mise à niveau vers Java 16 ou Java 17. Les tâches de mise à niveau les plus courantes sont abordées afin que vous puissiez les résoudre plus facilement et vous concentrer sur les défis spécifiques à votre application.
Pourquoi mettre à niveau ?
Chaque nouvelle version de Java, en particulier les plus importantes, contient des améliorations telles que la résolution de vulnérabilités de sécurité, les performances sont améliorées et de nouvelles fonctionnalités sont ajoutées. Garder Java à jour aide à maintenir une application saine. Cela peut également aider les organisations à conserver leurs employés et potentiellement à en attirer de nouveaux, car les développeurs sont généralement plus désireux de travailler sur des technologies plus récentes.
La mise à niveau est parfois considérée comme un défi
Il est perçu que la mise à niveau vers une version plus récente de Java peut nécessiter une quantité de travail importante. Cela est dû aux modifications requises dans la base de code et à l'installation de la dernière version de Java sur tous les serveurs qui construisent et exécutent l'application. Heureusement, certaines entreprises utilisent Docker, ce qui peut permettre aux équipes de mettre à niveau ces éléments elles-mêmes.
De nombreux développeurs voient le système de modules de Java 9, alias Jigsaw, comme un grand défi. Cependant, Java 9 ne vous obligeait pas à utiliser explicitement le système de modules. En fait, la plupart des applications exécutées sur Java 9 ou supérieur n'ont pas configuré de modules Java dans leur base de code.
L'estimation de la quantité de travail requise pour toute mise à niveau peut être un défi. Cela dépend de divers éléments tels que le nombre de dépendances et leur actualité. Si vous utilisez Spring Boot, par exemple, la mise à niveau de Spring Boot peut déjà résoudre la plupart des problèmes de mise à niveau. Malheureusement, en raison de l'incertitude, la plupart des développeurs estiment plusieurs jours, semaines ou même mois pour la mise à niveau. Cela peut amener l'organisation ou la direction à reporter la mise à niveau en raison du coût, du temps ou d'autres priorités. J'ai vu des estimations allant de semaines à mois pour mettre à niveau une application Java 8 vers Java 11. Cependant, j'ai réussi une mise à niveau similaire en quelques jours seulement. Cela était en partie dû à mon expérience précédente, cependant, il s'agit également de simplement commencer le processus de mise à niveau. C'est une tâche idéale du vendredi après-midi pour mettre à niveau Java et voir ce qui se passe. J'ai récemment migré une application Java 11 vers Java 16 et la seule tâche requise était de mettre à niveau la dépendance Lombok.
La mise à niveau peut être difficile et estimer le temps peut sembler impossible, mais souvent le processus de mise à niveau réel ne prend pas si longtemps. J'ai été témoin des mêmes problèmes lors de la mise à niveau de nombreuses applications. J'espère aider les équipes à résoudre rapidement les problèmes récurrents afin qu'elles puissent concentrer leurs efforts sur les défis spécifiques aux applications.
La cadence de publication de Java
Dans le passé, une nouvelle version de Java était publiée tous les deux ans. Depuis la sortie de Java 9, cependant, la cadence de publication est passée à tous les six mois avec une nouvelle version avec support à long terme (LTS) publiée tous les trois ans. La plupart des versions non LTS sont prises en charge avec des mises à jour mineures pendant environ six mois jusqu'à environ la sortie de la prochaine version. Les versions LTS, en revanche, reçoivent des mises à jour mineures pendant des années, au moins jusqu'à la prochaine version LTS. Selon le fournisseur OpenJDK (Adoptium, Azul, Corretto, etc.), le support peut en fait être disponible beaucoup plus longtemps. Azul, par exemple, offre un support plus long sur les versions non LTS.
Vous vous demandez peut-être : « Dois-je toujours mettre à niveau vers la dernière version ou rester sur une version LTS ? » Garder votre application sur une version LTS signifie que vous pouvez facilement profiter des mises à jour mineures avec les différentes améliorations notamment celles liées à la sécurité. D'un autre côté, lorsque vous utilisez la dernière version non-LTS, vous devez effectuer une mise à niveau vers une nouvelle version non-LTS tous les six mois, sinon les mises à niveau mineures ne seront plus disponibles.
La mise à niveau tous les six mois, cependant, peut être un défi car vous devrez peut-être attendre que vos frameworks aient également été mis à niveau avant de mettre à niveau votre application. Mais vous ne devriez pas attendre trop longtemps car les mises à niveau mineures pour ces versions non LTS ne sont plus publiées. Dans notre entreprise, nous avons décidé de rester sur les versions LTS pour l'instant car nous doutons d'avoir le temps de mettre à niveau tous les six mois dans un court laps de temps. Cependant, cela reste ouvert au débat, si une équipe le souhaite vraiment, ou si de nouvelles fonctionnalités Java intéressantes sont publiées pour une version non LTS, nous pourrions alors revoir notre décision.
Que mettre à niveau ?
En général, une application se compose de dépendances et de votre propre code qui est empaqueté et s'exécute en utilisant le JDK. Si quelque chose change dans le JDK, alors les dépendances, votre propre code - ou les deux - doivent être modifiés. Dans la plupart des cas, cela se produit lorsqu'une fonctionnalité est supprimée du JDK. Si vos dépendances utilisent une fonctionnalité supprimée du JDK , il est utile d'être patient et d'attendre qu'une nouvelle version de la dépendance soit publiée.
Multiple JDK
Lors de la mise à niveau de votre application, vous souhaiterez peut-être utiliser deux versions différentes du JDK, telles que la dernière version pour la mise à niveau réelle et une version plus ancienne pour maintenir votre application. La version actuelle du JDK utilisée pour le développement d'applications peut être spécifiée avec la variable d'environnement JAVA_HOME
ou des utilitaires de gestion de packages tels que SDKMAN ! ou JDKMon.
Pour les exemples de mon référentiel GitHub, j'utilise Docker pour montrer comment des fonctionnalités spécifiques fonctionnent, ou même s'interrompent, en utilisant différentes versions de JDK. Cela vous permettra de les essayer sans avoir à installer plusieurs versions du JDK. Malheureusement, la boucle de rétroaction avec les conteneurs Docker est un peu longue. Tout d'abord, l'image doit être créée et exécutée. Donc, en général, je recommande de mettre à niveau autant que possible à partir de votre IDE. Mais ce peut être une bonne idée d'essayer certaines choses ou de créer votre application dans un environnement propre sans paramètres personnels dans un conteneur Docker.
Pour le démontrer, créons un fichier Dockerfile
standard contenant le contenu indiqué ci-dessous. L'exemple utilise une image Maven JDK 17 et copie la source de votre application dans l'image. La commande RUN
exécute tous les tests et n'échouera pas en cas d'erreur.
FROM maven:3.8.1-openjdk-17-slim
ADD . /yourproject
WORKDIR /yourproject
RUN mvn test --fail-at-end
Pour créer l'image comme décrit ci-dessus, appelez la commande docker build
en ajoutant l'option -t
pour indiquer un tag (ou nom) et le .
pour configurer le contexte, qui dans ce cas est le répertoire courant.
docker build -t javaupgrade .
Les préparatifs
La plupart des développeurs commencent par mettre à niveau leur environnement local, suivi du serveur de build, puis des divers environnements de déploiement. Cependant, je crée parfois simplement un build sur le serveur de build avec une nouvelle version Java pour voir ce qui peut potentiellement mal tourner sans avoir à configurer toute la configuration pour ce projet spécifique.
Il est possible de passer de Java 8 à 17 à la fois. Cependant, si vous rencontrez des problèmes, il peut être difficile de déterminer quelle nouvelle fonctionnalité entre ces versions Java a causé le problème. La mise à niveau par étapes plus petites, par exemple de Java 8 à Java 11, facilite l'identification des problèmes. Cela aide également à rechercher le problème si vous pouvez ajouter la version Java qui a causé le problème à votre requête de recherche.
Tout d'abord, je vous recommande de mettre à niveau vos dépendances sur l'ancienne version de Java. De cette façon, vous pouvez vous concentrer sur le fonctionnement des dépendances sans mettre à jour Java en même temps. Malheureusement, ce n'est pas toujours possible car certaines dépendances peuvent nécessiter une version plus récente de Java. Si tel est le cas, vous n'avez pas d'autre choix que de mettre à niveau à la fois Java et les dépendances.
Il existe des plugins Maven et Gradle disponibles qui affichent les nouvelles versions de vos dépendances. La commande mvn versions:display-dependency-updates
appelle le plugin Maven Versions. Le plugin affiche chaque dépendance qui a une nouvelle version disponible :
[INFO] --- versions-maven-plugin:2.8.1:display-dependency-updates (default-cli) @ mockito_broken ---
[INFO] The following dependencies in Dependencies have newer versions:
[INFO] org.junit.jupiter:junit-jupiter .................... 5.7.2 -> 5.8.0-M1
[INFO] org.mockito:mockito-junit-jupiter ................... 3.11.0 -> 3.11.2
Alors que la commande gradle dependUpdates -Drevision=release
appelle le plugin Gradle Versions après avoir configuré le plugin dans le fichier build.gradle
:
plugins { id "com.github.ben-manes.versions" version "$version" }
Après avoir mis à niveau les dépendances, il est temps de mettre à niveau Java. Bien sûr, la modification du code pour qu'il fonctionne sur une nouvelle version de Java fonctionne mieux à partir d'un IDE, alors assurez-vous qu'il prend en charge la dernière version de Java. Après cela, mettez à niveau votre outil de build vers la dernière version et configurez la version Java :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
plugins {
java {
toolchain {
languageVersion = JavaLanguageVersion.of(16)
}
}
}
compile 'org.apache.maven.plugins:maven-compiler-plugin:3.8.1'
N'oubliez pas de mettre également à jour les plugins Maven et Gradle vers leurs dernières versions.
Les fonctionnalités supprimées du JDK
Il est toujours possible que des éléments soient supprimés du JDK. Ceux-ci incluent des méthodes, des certificats, des algorithmes de ramasse-miettes, des options de la JVM et même des outils complets. Dans la plupart des cas, cependant, ces éléments sont initialement dépréciés ou marqués pour suppression. Par exemple, JAXB était à l'origine obsolète dans Java 9 et finalement supprimé dans Java 11. Si vous avez déjà résolu des problèmes liés à des fonctionnalités obsolètes, vous n'aurez aucun souci une fois qu'elles seront effectivement supprimées.
Le Java Version Almanach ou le Foojay Almanac peut être utilisé comme référence pour comparer différentes versions de Java afin de voir quels éléments ont été ajoutés, obsolètes ou supprimés. Les modifications de niveau supérieur sous la forme de Java Enhancement Proposals (JEP) sont disponibles sur le site d'OpenJDK. Des informations plus détaillées sur chaque version de Java sont disponibles dans les release notes publiées par Oracle.
Java 11
Diverses fonctionnalités ont été supprimées de Java 11. Le premier est JavaFX, qui ne fait plus partie de la spécification ni n'est plus fourni avec OpenJDK. Cependant, il existe des fournisseurs proposant des versions du JDK qui incluent plus que ce qui est dans la spécification. Par exemple, ojdkbuild et le full JDK de Liberica JDK inclut tous les deux OpenJFX. Vous pouvez également utiliser la version JavaFX proposée par Gluon ou ajouter les dépendances OpenJFX à votre application.
Avant JDK 11, certaines polices étaient incluses dans le JDK. Par exemple, Apache POI a pu utiliser ces polices pour les documents Word et Excel. À partir du JDK 11, cependant, ces polices ne sont plus disponibles. Si le système d'exploitation ne les fournit pas non plus, vous pouvez rencontrer des erreurs étranges. La solution consiste à installer les polices sur le système d'exploitation. Selon les polices utilisées dans une application, vous devrez peut-être installer d'autres packages :
apt install fontconfig
Optional: libfreetype6 fontconfig fonts-dejavu
Java Mission Control (JMC), une application de surveillance et de profilage, permet de profiler les applications dans n'importe quel environnement, y compris en production avec un minimum d'overhead. Si vous ne l'avez pas essayé, je le recommande vivement. Il ne fait plus partie du JDK lui-même mais est fourni sous forme de téléchargements séparés sous le nouveau nom JDK Mission Control par AdoptOpenJDK et Oracle.
Le plus grand changement dans Java 11 a été la suppression des modules Java EE et CORBA tels que les quatre API de services Web - JAX-WS, JAXB, JAF et Common Annotations - qui ont été jugées redondantes car elles étaient déjà incluses dans Java EE. Oracle a fait don de Java EE 8 à la Fondation Eclipse peu de temps après sa sortie en 2017 avec l'intention que Java EE soit open source. En raison de la politique de marque d'Oracle, il était nécessaire de renommer Java EE en Jakarta EE et migrer l'espace de noms depuis javax
vers jakarta
. Ainsi, lorsque vous utilisez des dépendances telles que JAXB, assurez-vous d'utiliser les nouveaux artefacts Jakarta EE. Par exemple, la version Java EE 8 de l'artefact JAXB s'appelle javax.xml.bind:jaxb-api
et le développement futur s'est terminé en 2018. Le développement de la version Jakarta EE de JAXB continuera sous le nouveau nom d'artefact jakarta.xml.bind:jakarta.xml.bind-api
. Dans l'application, assurez-vous également de modifier toutes les importations vers le nouvel espace de noms jakarta
. Avec JAXB, par exemple : javax.xml.bind.*
vers jakarta.xml.bind.*
et ajouter les dépendances pertinentes.
L'image ci-dessous montre dans la colonne de gauche quels modules sont impactés par ce changement. Les deux colonnes de droite montrent les groupId
et artifactId
que vous pouvez utiliser comme dépendances. Veuillez noter que JAXB et JAX-WS nécessitent deux dépendances : une pour l'API et une pour l'implémentation. CORBA n'a pas d'alternative officielle à Jakarta, mais Glassfish fournit toujours un artefact que vous pouvez utiliser.
Java 15
Le moteur JavaScript Nashorn a été supprimé dans Java 15, mais vous pouvez toujours l'utiliser en ajoutant la dépendance :
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.2</version>
</dependency>
Java 16
Dans cette version, les développeurs du JDK ont encapsulé certains composants internes du JDK. Ils ne veulent plus que vous ou quelqu'un d'autre utilisiez les API de bas niveau du JDK. Cela a principalement impacté des outils comme Lombok. Heureusement, dans le cas de Lombok, une nouvelle version a été publiée en quelques semaines, ce qui a résolu le problème.
Si vous avez du code ou des dépendances qui utilisent toujours les composants internes du JDK, il est alors possible de les rouvrir. Cela peut sembler assez "sale" comme retirer une serrure d'une porte. Essayez de résoudre le problème en utilisant des API de plus haut niveau du JDK. Si ce n'est pas possible, cette solution de contournement est disponible dans Maven :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED</arg>
</compilerArgs>
</configuration>
</plugin>
J'ai essayé d'utiliser Maven Toolchains pour basculer entre les JDK en les spécifiant dans le fichier pom.xml
. Malheureusement, une erreur s'est produite lors de l'exécution de l'application en utilisant Java 16 avec une ancienne version de Lombok :
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project broken: Compilation failure -> [Help 1]
C'est toute l'information qui a été présentée. Je ne sais pas pour vous, mais pour moi, cela n'a pas été vraiment utile, alors j'ai déposé ce problème. Si le problème est résolu, l'utilisation de Maven Toolchains est un bon moyen de basculer entre les versions. Par la suite, j'ai exécuté le code directement sur Java 16 et j'ai obtenu une erreur plus descriptive qui mentionne une partie de la solution de contournement que j'ai montrée précédemment :
… class lombok.javac.apt.LombokProcessor (in unnamed module @0x21bd20ee) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment
(in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing
to unnamed module …
Java 17
Les mainteneurs du JDK se sont déjà mis d'accord sur le contenu pour la sortie en septembre. L'API Applet sera dépréciée car les navigateurs ont cessé de prendre en charge les applets il y a longtemps. Le compilateur expérimental AOT et JIT sera également supprimé. Comme alternative au compilateur expérimental, vous pouvez utiliser GraalVM. Le plus gros changement est la JEP-403 : Strongly Encapsulate JDK internals. L'option Java --illegal-access
ne fonctionne plus et si vous accédez toujours à une API interne, l'exception suivante est levée :
java.lang.reflect.InaccessibleObjectException:
Unable to make field private final {type} accessible:
module java.base does not "opens {module}" to unnamed module {module}
La plupart du temps, cela peut être résolu en mettant à niveau vos dépendances ou en utilisant des API de plus haut niveau. Si ce n'est pas possible, vous pouvez utiliser l'argument --add-opens
pour donner à nouveau accès aux API internes. Cependant, cela ne devrait être utilisé qu'en dernier recours.
Sachez que certains outils ne fonctionneront pas encore sur Java 17. Par exemple, Gradle ne construira pas votre projet et Kotlin ne peut pas utiliser jvmTarget = "17"
. Certains frameworks, tels que Mockito, ont rencontré quelques petits problèmes sur Java 17. Par exemple, les méthodes dans enum
ont causé ce problème particulier. Cependant, je m'attends à ce que la plupart des problèmes soient résolus avant ou légèrement après la sortie de Java 17.
Vous pouvez voir le message "Unsupported class file major version 61" pour l'un de vos plugins ou dépendances lors de la construction de l'application. La version majeure des fichierse class 61 est utilisée pour Java 17, 60 a été utilisée pour Java 16. Cela signifie essentiellement que le plug-in ou la dépendance ne fonctionne pas sur cette version de Java. La plupart du temps, la mise à niveau vers la dernière version résout le problème.
La finalisation
Après avoir résolu tous les défis, vous pouvez enfin exécuter votre application sur Java 17. Après tout le travail acharné, vous pouvez désormais utiliser de nouvelles fonctionnalités Java passionnantes telles que les Records et le Pattern Matching.
Conclusion
La mise à niveau de Java peut être un défi en fonction de l'ancienneté de votre version et de vos dépendances Java, et de la complexité de votre configuration. Ce guide aide à résoudre les problèmes les plus courants lors de la mise à niveau de Java. En général, il est difficile d'estimer combien de temps la mise à niveau prendra réellement. Cependant, j'ai constaté que ce n'est souvent pas aussi difficile que certains développeurs peuvent le penser. La plupart du temps, je dirais qu'une mise à niveau de Java 11 vers Java 17 est plus facile que de Java 8 vers Java 11. Pour la plupart des applications, la migration d'un LTS au suivant prend de quelques heures à quelques jours. La plupart du temps est consacré à la construction de l'application. Il est important de commencer et d'apporter des changements progressifs. De cette façon, vous pouvez vous motiver, votre équipe et votre direction à continuer à y travailler.
Avez-vous déjà commencé à mettre à niveau vos applications ?
A propos de l'auteur
Johan Janssen est architecte logiciel dans le secteur de l'éducation pour Sanoma Learning. Il aime partager des connaissances principalement autour de Java. A parlé lors de conférences telles que Devoxx, Oracle Code One, Devnexus et bien d'autres. Il a assisté des conférences en participant à des comités de programme et il a créé et organisé JVMCON. Il a reçu les prix JavaOne Rock Star et Oracle Code One Star. Il a rédigé divers articles pour des médias numériques et imprimés. Il maintient divers packages Java JDK/JRE pour Chocolatey avec environ 100 000 téléchargements par mois.