Java EE / Jakarta EE

Architecture Jakarta EE : conteneurs, profils et serveurs d'applications

Débutant14 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

Jakarta EE est un ensemble de spécifications qu'un serveur d'applications implémente pour que votre code s'appuie sur des services gérés au lieu de tout câbler lui-même. À l'issue de ce guide, vous saurez expliquer le modèle de conteneurs, citer les spécifications qui composent Jakarta EE 10 et ses profils, comprendre ce qu'a changé le passage de javax.* à jakarta.*, packager correctement un WAR ou un EAR et choisir entre GlassFish, WildFly, Open Liberty, Payara ou un simple conteneur de servlets.

Ce que vous allez apprendre

  • Décrire le modèle multi-tiers et le rôle du conteneur web et du conteneur d'enterprise beans
  • Citer les principales spécifications de Jakarta EE 10 et le contenu des profils Platform, Web et Core
  • Retracer l'histoire de J2EE à Jakarta EE et la migration de javax.* vers jakarta.*
  • Packager une application en WAR ou en EAR et comprendre les noms JNDI portables
  • Choisir un serveur d'applications ou un runtime pour un projet neuf ou existant

Toute application serveur un tant soit peu sérieuse a besoin du même lot de services : une façade HTTP, un moyen de parler à une base de données dans une transaction, un modèle de sécurité, un endroit où exécuter des traitements en arrière-plan et une forme de messagerie. Jakarta EE est la réponse standardisée à cette liste sur la plateforme Java. Vous écrivez des composants en vous appuyant sur un ensemble d'API publiées ; un serveur d'applications implémente ces API et fournit les services d'exécution, si bien que votre code ne contient presque plus de plomberie d'infrastructure. Comprendre ce partage des responsabilités — ce que le conteneur fait pour vous et ce qu'il attend de vous — est la base de tout le reste de ce sujet, et éclaire aussi en bonne partie la conception de Spring et de MicroProfile.

De J2EE à Jakarta EE

La plateforme a changé de nom deux fois et ces noms sèment encore la confusion ; mieux vaut donc poser la chronologie.

VersionAnnéeContenu marquant
J2EE 1.2 – 1.41999 – 2003Servlets, JSP, EJB 1.x/2.x avec interfaces home et descripteurs XML, JMS, JTA, JAX-RPC
Java EE 52006Les annotations remplacent la plupart des descripteurs, EJB 3.0, Java Persistence API (JPA) 1.0, JSF 1.2
Java EE 62009CDI 1.0, EJB 3.1 (vue sans interface, singletons), JAX-RS 1.1, Bean Validation, le Web Profile
Java EE 72013API simplifiée de JMS 2.0, WebSocket, JSON-P, Batch, Concurrency Utilities, @Transactional de JTA
Java EE 82017JSON-B, Java EE Security API 1.0, Servlet 4.0 avec le push HTTP/2, JAX-RS 2.1
Jakarta EE 82019Mêmes API que Java EE 8, désormais gouvernées par la fondation Eclipse ; packages javax.*
Jakarta EE 9 / 9.12020 / 2021Espace de noms des packages changé en jakarta.* ; suppression des anciennes API élaguées ; support de Java 11
Jakarta EE 102022Première version à apporter des nouveautés sous Eclipse : CDI 4.0 avec CDI Lite, Core Profile, Servlet 6, Faces 4, Persistence 3.1, REST 3.1
Jakarta EE 112025Java 17 comme socle minimal, Jakarta Data 1.0, nouvel élagage d'API historiques

Le changement de nom vient de ce qu'Oracle a transféré en 2017 les spécifications et le code de référence à la fondation Eclipse tout en conservant la marque Java. Le préfixe de package javax relevant de cette marque, la communauté n'a eu d'autre choix que de renommer tous les packages. Voilà pourquoi Jakarta EE 9 casse la compatibilité sans apporter la moindre nouveauté : javax.servlet.http.HttpServletRequest est devenu jakarta.servlet.http.HttpServletRequest, javax.persistence.Entity est devenu jakarta.persistence.Entity, et ainsi de suite pour chaque spécification. Les espaces de noms XML des descripteurs de déploiement ont changé en même temps, de http://xmlns.jcp.org/xml/ns/javaee à https://jakarta.ee/xml/ns/jakartaee.

Ce guide cible Jakarta EE 10 et utilise jakarta.* partout. Quand vous lisez des ressources plus anciennes — et la plupart du code Java d'entreprise en production a été écrit pour Java EE 6, 7 ou 8 —, remplacez mentalement le préfixe : les noms de classes et de méthodes sont par ailleurs identiques.

Le modèle en tiers et en conteneurs

Jakarta EE décrit une application comme un ensemble de composants répartis sur des tiers logiques. Ces tiers sont conceptuels : deux d'entre eux partagent souvent le même processus, et dans la plupart des déploiements modernes, tous les tiers côté serveur vivent dans une seule instance de serveur d'applications.

  • Tier client — un navigateur, une application mobile, un autre service qui appelle un endpoint REST, ou un client Java de bureau. Pour ce dernier cas, la plateforme définit toujours un conteneur de client applicatif, mais il ne sert plus guère aujourd'hui.
  • Tier web — servlets, pages Faces, ressources Jakarta REST et endpoints WebSocket. Ces composants reçoivent les requêtes, les traduisent en appels vers le tier métier et produisent la réponse.
  • Tier métier — enterprise beans et beans CDI qui implémentent les cas d'usage, s'exécutent dans des transactions et appliquent les règles de sécurité.
  • Tier du système d'information d'entreprise (EIS) — bases de données relationnelles, brokers de messages, mainframes et autres back-ends atteints via JDBC, JMS ou des resource adapters de connecteurs (JCA).

Ce qui en fait plus qu'un simple schéma d'architecture en couches, c'est le conteneur : le runtime qui héberge un type de composant et s'interpose entre ce composant et le monde extérieur. Comme votre servlet ou votre bean est instancié et invoqué par le conteneur, celui-ci peut :

  • injecter les dépendances (@Inject, @EJB, @PersistenceContext, @Resource) avant que votre code ne s'exécute ;
  • démarrer et valider une transaction autour de chaque méthode métier ;
  • vérifier que l'appelant possède le rôle requis ;
  • mettre les instances en pool, gérer l'accès concurrent des threads et passiver l'état inactif ;
  • appliquer les intercepteurs et les callbacks de cycle de vie (@PostConstruct, @PreDestroy).

Le conteneur web héberge servlets, filtres, listeners, pages JSP, vues Faces, ressources REST et endpoints WebSocket. Le conteneur d'enterprise beans héberge les session beans et les message-driven beans. Tous deux partagent le conteneur CDI pour l'injection de dépendances et les scopes contextuels, ainsi que des services de plateforme comme le nommage JNDI, les transactions JTA et le sous-système de sécurité. Conséquence pratique : n'instanciez jamais un composant géré avec new. Un new OrderService() est un objet ordinaire, sans champ injecté, sans transaction ni contexte de sécurité ; seules les instances créées par le conteneur en bénéficient.

Ce que le serveur fournit réellement

Un serveur d'applications est le produit qui implémente les spécifications. Les éditeurs se font concurrence sur l'outillage d'administration, le clustering, les performances et le support, pas sur les API avec lesquelles votre code compile. Parmi les services que tout serveur conforme doit fournir :

ServiceSpécificationÀ quoi il vous sert
NommageJNDI (inclus dans Java SE)Rechercher par nom des data sources, des destinations JMS et des beans
TransactionsJakarta TransactionsFrontières ACID déclaratives ou programmatiques, y compris XA sur plusieurs ressources
PersistanceJakarta PersistenceMapping objet-relationnel avec un EntityManager géré par le conteneur
MessagerieJakarta MessagingQueues et topics, message-driven beans
WebServlet, Pages, Faces, REST, WebSocketTraitement des requêtes HTTP et rendu
Injection et scopesCDI, Dependency InjectionAssemblage des composants, scopes request/session/application, événements
SécuritéJakarta Security, Authentication, AuthorizationMécanismes d'authentification, identity stores, contrôles de rôles
ConcurrenceJakarta ConcurrencyExecutors gérés et tâches planifiées qui conservent le contexte du conteneur
IntégrationConnectors (JCA), MailResource adapters pour les systèmes historiques, envoi d'e-mails

Les ressources comme les data sources se configurent dans le serveur et sont exposées à votre code via des noms JNDI : le même WAR tourne ainsi en test et en production avec des identifiants différents, sans rebuild. @DataSourceDefinition permet à une application de déclarer sa propre data source, ce qui est pratique pour une démo mais place les identifiants dans le code.

import jakarta.annotation.Resource;
import jakarta.enterprise.context.ApplicationScoped;
import javax.sql.DataSource;

@ApplicationScoped
public class HealthCheck {

    // Le conteneur résout ce nom JNDI au moment de l'injection.
    @Resource(lookup = "java:/jdbc/OrdersDS")
    DataSource dataSource;

    public boolean databaseReachable() {
        try (var connection = dataSource.getConnection()) {
            return connection.isValid(2);
        } catch (Exception e) {
            return false;
        }
    }
}

Les profils : Platform, Web et Core

Toutes les applications n'ont pas besoin de tout cela, et imposer un serveur de plateforme complète pour une petite application web est l'une des raisons pour lesquelles Java EE a perdu du terrain face à des stacks plus légères dans les années 2000. Depuis Java EE 6, la plateforme définit des profils, et Jakarta EE 10 en compte trois :

  • Jakarta EE Platform — tout.
  • Web Profile — le tier web plus ce dont une application web typique a besoin derrière : Servlet, Pages, Expression Language, Faces, CDI, Bean Validation, Persistence, Transactions, REST, JSON-P/JSON-B, WebSocket, Security, Concurrency (ajoutée au profil avec EE 10) et EJB Lite : session beans locaux, transactions gérées par le conteneur, intercepteurs, sécurité déclarative et timers non persistants, mais ni interfaces distantes, ni message-driven beans, ni JMS.
  • Core Profile — nouveau dans EE 10, destiné aux frameworks de microservices et à la compilation ahead-of-time : CDI Lite, REST, JSON-P, JSON-B, Annotations, Dependency Injection et Interceptors. MicroProfile s'appuie sur ce profil.

Ce choix détermine votre dépendance Maven. Compilez avec l'artefact d'API du profil visé et laissez le serveur le fournir à l'exécution :

<dependency>
  <groupId>jakarta.platform</groupId>
  <artifactId>jakarta.jakartaee-web-api</artifactId>  <!-- ou jakarta.jakartaee-api pour la plateforme complète -->
  <version>10.0.0</version>
  <scope>provided</scope>
</dependency>

Le scope provided n'est pas un détail. Si vous embarquez les classes d'API — ou pire, une implémentation comme Hibernate — dans un WAR déployé sur un serveur qui les fournit déjà, vous obtenez des conflits de chargement de classes pénibles à diagnostiquer.

Packaging et déploiement

Les applications Jakarta EE se déploient sous forme d'archives à la structure interne imposée, et le conteneur lit à la fois les annotations et d'éventuels descripteurs XML pour découvrir les composants.

  • WAR (web archive) — WEB-INF/classes pour vos classes, WEB-INF/lib pour les bibliothèques, WEB-INF/web.xml (facultatif depuis Servlet 3.0), WEB-INF/beans.xml (facultatif avec CDI 4, qui découvre par défaut les beans annotés), META-INF/persistence.xml dans classes pour JPA. Depuis Java EE 6, un WAR peut contenir directement des enterprise beans ; c'est de loin le packaging le plus courant aujourd'hui.
  • EJB JAR — uniquement des enterprise beans, avec un META-INF/ejb-jar.xml facultatif.
  • EAR (enterprise archive) — une enveloppe contenant des WAR, des EJB JAR, des resource adapters (RAR) et des bibliothèques partagées dans lib/, décrite par META-INF/application.xml. Ne l'utilisez que si plusieurs modules doivent réellement partager des bibliothèques et être déployés comme une seule unité ; il complique le chargement des classes et la plupart des équipes n'en ont plus besoin.

L'en-tête d'un web.xml Servlet 6.0 montre l'espace de noms et la version actuels :

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                             https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
  <session-config>
    <session-timeout>30</session-timeout>
  </session-config>
</web-app>

Les descripteurs priment sur les annotations : c'est la manière prévue de modifier des réglages propres à un environnement — une contrainte de sécurité, un paramètre d'initialisation de servlet, un timeout de session — sans toucher au code source.

Les noms JNDI portables

Depuis Java EE 6, chaque session bean reçoit des noms JNDI standard, ce qui permet de le rechercher depuis n'importe où sans syntaxe propre à un éditeur :

java:global/<app-name>/<module-name>/<bean-name>[!<fully-qualified-interface>]
java:app/<module-name>/<bean-name>
java:module/<bean-name>

Pour un bean OrderService dans shop.war déployé seul, le nom global est java:global/shop/OrderService. L'espace de noms java:comp/env contient les entrées d'environnement propres au composant (points d'injection @Resource), et java:comp/UserTransaction et java:comp/DefaultDataSource sont des entrées standard que tout serveur fournit. L'injection a rendu les lookups explicites rares, mais vous en croiserez encore dans du code ancien et dans les clients distants.

Choisir un serveur ou un runtime

Tous les produits compatibles Jakarta EE 10 passent le même Technology Compatibility Kit : le comportement des API est donc identique, et les différences sont d'ordre opérationnel.

ProduitProfilRemarques
Eclipse GlassFish 7PlatformL'implémentation compatible utilisée pour ratifier les spécifications ; pratique pour apprendre, moins répandue en production
WildFly (27+)PlatformLe serveur communautaire de Red Hat, base de JBoss EAP ; propose aussi un feature pack « preview » et un packaging en bootable JAR
Open LibertyPlatformLe runtime modulaire d'IBM ; les fonctionnalités s'activent par application, démarrage rapide, base de WebSphere Liberty
Payara Server / MicroPlatformDérivé de GlassFish avec support commercial ; Payara Micro exécute un WAR depuis la ligne de commande
Apache TomEE 10Variantes Web Profile / PlatformTomcat plus OpenEJB, OpenJPA/Hibernate, ActiveMQ, CXF
Apache Tomcat 10.1 / Jetty 12Servlet, Pages, EL, WebSocket seulementPas des serveurs Jakarta EE ; vous apportez le reste sous forme de bibliothèques

En pratique, le choix dépend de ce que votre équipe d'exploitation fait déjà tourner et maintient à jour, de votre besoin ou non d'un support commercial (JBoss EAP, WebSphere Liberty et Payara Enterprise sont les éditions supportées de WildFly, Open Liberty et Payara), et de votre mode de déploiement : tous ces serveurs peuvent être intégrés à une image de conteneur avec l'application, et le bootable JAR de WildFly ou Payara Micro produisent un exécutable unique, un peu comme un jar Spring Boot. Si vous n'utilisez que les servlets, JPA et REST, un runtime Web Profile — ou Tomcat accompagné de bibliothèques — peut être la réponse pragmatique.

La place de Spring et de MicroProfile

Spring est né en 2003 en réaction à EJB 2.x, dont les interfaces home, les exceptions checked obligatoires et les descripteurs XML rendaient laborieuses les choses simples. Java EE 5 et 6 ont repris l'essentiel des idées de Spring — annotations, injection de dépendances, composants en objets ordinaires — dans EJB 3 et CDI. Aujourd'hui, la relation relève de la symbiose plus que de la concurrence : Spring Framework 6 et Spring Boot 3 dépendent des API Jakarta EE 9+ pour la couche servlet, JPA, Bean Validation, JMS et JTA, et fournissent simplement leur propre conteneur au lieu de s'appuyer sur un serveur d'applications. Si vous comprenez les spécifications, vous comprenez une bonne partie de ce que Spring auto-configure ; le sujet Spring couvre ce versant.

MicroProfile est un ensemble voisin de spécifications Eclipse qui ajoute, au-dessus du Core Profile, ce dont les services cloud-native ont besoin — configuration, health checks, métriques, OpenAPI, client REST, tolérance aux pannes, propagation de JWT — et qu'implémentent Open Liberty, Payara, WildFly, Quarkus et Helidon. Ce que vous savez de CDI et de Jakarta REST vous resservira avec chacun d'eux.

Migrer de javax à jakarta

Le renommage de Jakarta EE 9 étant mécanique, l'outillage peut en faire l'essentiel :

  1. Mettre à niveau le runtime et la dépendance d'API

    Passez à un serveur Jakarta EE 9.1+ (ou Tomcat 10.1+, Jetty 11+) et faites passer la dépendance jakarta.jakartaee-api (ou les dépendances par spécification) à une version 9.1 ou 10.
  2. Réécrire les imports et les descripteurs

    Lancez Eclipse Transformer ou une recette OpenRewrite sur l'arborescence des sources. Ils réécrivent les imports javax.* des packages concernés, les espaces de noms XML et les noms de propriétés comme javax.persistence.jdbc.url, sans toucher aux packages Java SE comme javax.sql et javax.naming, qui n'ont jamais bougé.
  3. Mettre à jour chaque bibliothèque qui touche aux API

    Hibernate 6, Jersey 3, RESTEasy 6, Mojarra 4, EclipseLink 4 et Spring 6 sont les lignes basées sur l'espace de noms Jakarta. Une seule dépendance transitive qui importe encore javax.servlet échouera à l'exécution avec NoClassDefFoundError : passez l'arbre des dépendances au crible.
  4. Traquer les API supprimées

    Servlet 6.0 a supprimé des types dépréciés de longue date comme SingleThreadModel et HttpSessionContext ; Faces 4.0 a supprimé @ManagedBean et les vues JSP ; Enterprise Beans 4.0 a abandonné les entity beans EJB 2.x et les endpoints JAX-RPC. Le code qui les utilise doit être réécrit, pas simplement réimporté.

Le guide de migration de Tomcat décrit une variante de la même démarche : Tomcat 10 sait transformer à la volée les WAR historiques, ce qui fait gagner du temps mais ne doit pas devenir la solution durable.

Erreurs courantes

  • Embarquer une implémentation que le serveur fournit déjà. Packager hibernate-core ou une implémentation de Jakarta REST dans un WAR destiné à WildFly ou Liberty produit deux copies des mêmes classes dans des class loaders différents, et des erreurs comme ClassCastException: OrderEntity cannot be cast to OrderEntity. Utilisez le scope provided pour les API et laissez le serveur fournir les implémentations, sauf si sa documentation explique comment remplacer sa version.
  • Mélanger des artefacts javax et jakarta. Un projet à moitié migré compile, puis échoue au déploiement. Fouillez l'arbre des dépendances, pas seulement vos propres imports.
  • Traiter les tiers comme physiques. Séparer le tier web et le tier métier sur des serveurs distincts reliés par des appels distants d'enterprise beans était à la mode à l'époque de J2EE ; c'est presque toujours plus lent et plus difficile à exploiter qu'un seul processus. Gardez-les ensemble, sauf si des mesures justifient le contraire.
  • Configurer les ressources dans l'application. Des URL de connexion codées en dur dans persistence.xml ou @DataSourceDefinition fonctionnent sur un poste de développement, puis font fuiter des identifiants dans le gestionnaire de versions. Définissez data sources et destinations JMS dans le serveur et référencez-les par leur nom JNDI.
  • Utiliser un EAR par habitude. Un WAR unique est plus simple à construire, à déployer et à déboguer. Ne recourez à un EAR que si plusieurs modules doivent être versionnés ensemble.

Points clés à retenir

  • Jakarta EE est un ensemble de spécifications ; un serveur d'applications les implémente et exécute vos composants dans des conteneurs qui injectent les dépendances, gèrent les transactions et appliquent la sécurité.
  • La filiation : J2EE, puis Java EE 5 à 8, puis Jakarta EE 8 (mêmes API), 9 (javax.* devient jakarta.*, aucune nouveauté), 10 (première version à en apporter) et 11.
  • Jakarta EE 10 définit trois profils — Platform, Web et Core — pour ne cibler que ce dont vous avez besoin ; le Web Profile couvre la plupart des applications web.
  • Compilez avec l'artefact jakarta.jakartaee-api ou jakarta.jakartaee-web-api en scope provided et laissez le serveur fournir les implémentations.
  • Packagez en WAR dès que possible ; utilisez les noms JNDI portables java:global et des ressources définies côté serveur plutôt qu'une configuration codée en dur.
  • GlassFish, WildFly, Open Liberty, Payara et TomEE sont des implémentations compatibles ; Tomcat et Jetty n'implémentent que la famille Servlet.
  • Spring et MicroProfile reposent sur les mêmes spécifications : les connaissances se transfèrent dans les deux sens.

Sources et lectures complémentaires

Questions fréquentes

Jakarta EE, est-ce la même chose que Java EE ?
C'est la suite de Java EE sous l'égide de la fondation Eclipse. Jakarta EE 8 est fonctionnellement identique à Java EE 8 et utilise encore les packages javax.*. À partir de Jakarta EE 9, les packages sont passés à jakarta.*, ce qui explique pourquoi les bibliothèques et serveurs écrits pour Java EE 8 ne tournent pas tels quels sur un serveur Jakarta EE 9 ou plus récent.
Ai-je besoin d'un serveur d'applications, ou Tomcat suffit-il ?
Tomcat et Jetty n'implémentent que les spécifications Servlet, JSP, EL et WebSocket. Si vous attendez du conteneur lui-même CDI, JPA, JTA, JMS, les enterprise beans ou Jakarta REST, prenez un serveur Web Profile ou Platform complet comme WildFly, Open Liberty, Payara, GlassFish ou TomEE. Des frameworks comme Spring Boot embarquent leurs propres implémentations de plusieurs de ces spécifications et peuvent donc tourner sur Tomcat.
Quelle différence entre le Web Profile et la Platform complète ?
Le Web Profile est un sous-ensemble destiné aux applications web : servlets, Faces, CDI, JPA, JTA, Bean Validation, Jakarta REST, JSON, WebSocket, Security, Concurrency et un jeu réduit de fonctionnalités d'enterprise beans appelé EJB Lite. La Platform complète y ajoute la messagerie (JMS), les connecteurs, Mail, Batch, les beans distants et message-driven, entre autres. Le Core Profile, apparu avec Jakarta EE 10, est encore plus réduit et vise les runtimes de microservices.
Un nouveau projet doit-il partir sur Jakarta EE ou sur Spring Boot ?
Les deux se défendent. Jakarta EE vous apporte des standards indépendants des éditeurs, un runtime géré et moins de code de framework dans votre application ; Spring Boot offre un écosystème plus large, un rythme de versions plus soutenu et un packaging autonome plus simple. Spring 6 repose lui-même sur les API Jakarta EE 9+ (Servlet, JPA, Bean Validation) : les spécifications que vous apprenez ici se transposent donc directement.