Jakarta EE Overview

What Jakarta EE is, how it relates to Spring, why the javax→jakarta rename happened, and what a "Bean" actually means in this ecosystem

← Back to Index

What is Jakarta EE β€” and Why Should a Spring Developer Care?

Jakarta EE is a collection of specifications β€” not a library, not a framework, not a product. Each specification defines an API (interfaces, annotations, contracts) that vendors implement. JPA defines @Entity and EntityManager; Hibernate is one implementation. Servlet defines HttpServletRequest; Tomcat is one implementation. The spec itself ships no runnable code.

This distinction matters because you are already using Jakarta EE specifications in every Spring Boot project. When you write @Entity, @NotBlank, or HttpServletRequest, those annotations come from Jakarta EE specs (jakarta.persistence, jakarta.validation, jakarta.servlet). Spring doesn't reinvent these β€” it implements or builds on top of them. Understanding Jakarta EE is understanding the foundations Spring sits on.

Jakarta EE vs Spring β€” not competing, layered
LayerWho provides itExample
Specification (API) Jakarta EE jakarta.persistence.@Entity
Implementation Hibernate, Tomcat, Weld… Hibernate persists the entity to the DB
Abstraction on top Spring / Spring Data JpaRepository wraps EntityManager

Spring Boot doesn't replace Jakarta EE specs β€” it packages implementations of them (Hibernate, Tomcat, Hibernate Validator) and adds its own abstraction layer on top. That's the whole model.

History — Java EE, the Oracle Handoff, and the javax→jakarta Rename

/*
 * 1999  J2EE 1.2 β€” first enterprise platform, Servlets + EJB + JMS
 * 2006  Java EE 5 β€” annotations replace XML-heavy config; JPA introduced
 * 2009  Java EE 6 β€” CDI introduced; EJB simplified; REST (JAX-RS)
 * 2013  Java EE 7 β€” WebSocket, JSON-P, Batch
 * 2017  Java EE 8 β€” HTTP/2, JSON-B, Security API
 *                    Oracle donates the platform to the Eclipse Foundation
 *                    β†’ Project renamed to Jakarta EE
 * 2019  Jakarta EE 8 β€” identical to Java EE 8, just new governance
 * 2020  Jakarta EE 9 β€” THE BREAKING CHANGE: javax.* β†’ jakarta.*
 * 2022  Jakarta EE 10 β€” Java 11+, CDI Lite, Core Profile
 * 2024  Jakarta EE 11 β€” Java 21+, Virtual Threads, JPA 3.2
 */
The javax→jakarta rename — why it happened and why it broke things

Oracle donated the Java EE codebase to the Eclipse Foundation in 2017, but retained the trademark on the javax namespace. The Eclipse Foundation couldn't add new APIs or fix existing ones under javax.* without Oracle's explicit permission for each change β€” a governance deadlock. The only way forward was renaming the namespace to one the Eclipse Foundation actually owned: jakarta.*.

This was a breaking change. Any code, library, or framework that imported javax.servlet.*, javax.persistence.*, javax.validation.* had to update its imports. Spring Framework 6 / Spring Boot 3 made exactly this migration β€” which is why all code in this Bible uses jakarta.* and why Boot 2.x code with javax.* is a different, incompatible world.

// Before Jakarta EE 9 / Spring Boot 2.x
import javax.persistence.Entity;
import javax.validation.constraints.NotBlank;
import javax.servlet.http.HttpServletRequest;

// Jakarta EE 9+ / Spring Boot 3+
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotBlank;
import jakarta.servlet.http.HttpServletRequest;

What is a Bean?

The word "bean" is overloaded across Java's history and means different things in different contexts. Before conflating them, it helps to distinguish them clearly:

TypeWhat it isWho manages itKey annotation
JavaBean A plain Java class with a no-arg constructor, private fields, and public getters/setters β€” a convention, not a framework concept Nobody β€” you manage it with new None
CDI Bean Any class the CDI container discovers, instantiates, and wires β€” the Jakarta EE standard for managed objects CDI container (Weld, OpenWebBeans) @ApplicationScoped, @RequestScoped, etc.
Spring Bean Any object managed by the Spring IoC container β€” same concept as CDI, different implementation Spring ApplicationContext @Component, @Service, @Bean
EJB A CDI bean with additional container-managed services: distributed transactions, security, async, timers EJB container (part of Jakarta EE server) @Stateless, @Stateful, @MessageDriven
JPA Entity A class mapped to a database table β€” managed by the JPA persistence context, not the CDI or Spring container JPA EntityManager @Entity
The common thread: container management, not just annotation presence

What makes something a "bean" in any of these contexts is not having an annotation β€” it's being managed by a container. The container creates the instance, resolves and injects its dependencies, calls lifecycle callbacks (@PostConstruct, @PreDestroy), and destroys it when appropriate. The critical implication: you don't use new to get a bean. If you instantiate a CDI or Spring bean with new, you get an object β€” but none of the container's services apply to it. No injection, no transaction management, no interceptors.

// CDI bean β€” container creates, injects, and manages lifecycle
@ApplicationScoped
public class UserService {

    @Inject
    private UserRepository repository;  // injected by container β€” never null after @PostConstruct

    @PostConstruct  // called AFTER injection is complete β€” safe to use injected fields here
    public void init() {
        log.info("UserService ready, repository={}", repository);
    }

    @PreDestroy  // called before container destroys the instance β€” clean up resources
    public void cleanup() {
        // release resources if needed
    }
}

// WRONG β€” this is just a regular object, no injection, no lifecycle
UserService svc = new UserService();  // repository is null β€” will NPE on first call

CDI Scopes β€” How Long a Bean Lives

@RequestScoped      // one instance per HTTP request β€” destroyed when request ends
@SessionScoped      // one instance per HTTP session β€” destroyed when session expires
@ApplicationScoped  // one instance for the whole application β€” effectively a singleton
@ConversationScoped // developer-controlled scope β€” spans multiple requests explicitly
@Dependent          // default: same scope as the bean that injects it (no shared instance)
CDI doesn't inject the real object β€” it injects a proxy

When a wider-scoped bean (e.g. @ApplicationScoped) injects a narrower-scoped bean (e.g. @RequestScoped), CDI cannot inject the actual @RequestScoped instance β€” it doesn't exist yet at injection time, and it changes per request. Instead, CDI injects a client proxy: a generated subclass that forwards every method call to the correct real instance for the current context. This is transparent to your code, but it imposes three requirements on any proxied class: it must not be final, its methods must not be final, and it must have a no-arg constructor (can be package-private). Violating any of these produces a CDI deployment error at startup, not a runtime exception.

Jakarta EE Specifications You Already Use in Spring Boot

The following Jakarta EE specs are active in every standard Spring Boot 3.x project β€” not optionally, by default. This is the concrete answer to "why does this topic matter if I'm a Spring developer":

SpecPackageUsed in Spring Boot via
Servlet 6.1 jakarta.servlet.* Every HTTP request β€” DispatcherServlet is a HttpServlet
JPA 3.2 jakarta.persistence.* @Entity, @Id, EntityManager β€” Hibernate implements it
Bean Validation 3.0 jakarta.validation.* @NotBlank, @Email, @Valid β€” Hibernate Validator implements it
CDI 4.1 (partial) jakarta.inject.* @Inject β€” Spring supports it as an alternative to @Autowired
Transactions (JTA) jakarta.transaction.* @Transactional β€” Spring's transaction manager implements JTA semantics
Quarkus uses Jakarta EE specs directly β€” no Spring layer

Quarkus (Red Hat's cloud-native framework) implements Jakarta EE specs directly β€” CDI for injection, JAX-RS for REST, JPA for persistence β€” without the Spring abstraction layer. If you encounter Quarkus in the wild or in a job description, understanding this page is what makes Quarkus's annotations readable: @Path is JAX-RS, @ApplicationScoped is CDI, @Inject is Jakarta Inject β€” all covered in the pages of this topic.

Application Servers vs Embedded Server β€” The Deployment Model Difference

/*
 * JAKARTA EE (traditional) β€” external application server:
 *
 *   [WildFly / Payara]
 *       └── runs permanently
 *       └── you deploy a WAR/EAR into it
 *       └── server provides: CDI, JPA, Transactions, Security, JMS...
 *       └── multiple apps can share one server
 *
 * SPRING BOOT β€” embedded server model:
 *
 *   [Your JAR]
 *       └── contains an embedded Tomcat/Jetty/Undertow
 *       └── java -jar myapp.jar β†’ app starts its own server
 *       └── one app per JVM process (the 12-factor way)
 *       └── no external server dependency at runtime
 */
AspectJakarta EE (external server)Spring Boot (embedded)
Deployment artifact WAR or EAR file Executable JAR (fat jar)
Runtime Deploy to running WildFly/Payara java -jar app.jar β€” self-contained
Container lifecycle Server manages everything Spring manages its own context
Shared resources Multiple apps share connection pools, JMS, etc. Each app owns its resources
Cloud / containers Possible but heavier Native fit β€” one process, one container image
Typical use today Legacy enterprise systems, banking, government New microservices, cloud-native apps
Jakarta EE also has an embedded story β€” Quarkus and Helidon

The "Jakarta EE requires a heavy application server" stereotype is outdated. Quarkus and Helidon both implement Jakarta EE specs and compile to lightweight executable JARs (Quarkus even supports GraalVM native image). The deployment model is now a choice, not a constraint of using Jakarta EE specs. What remains true is that traditional projects using WildFly or Payara still follow the WAR/EAR model β€” that's what you'll encounter in legacy enterprise systems.

JSON in Jakarta EE β€” JSON-P, JSON-B, and Why Jackson Won

Jakarta EE defines two specs for JSON:

SpecPackageLevelEquivalent in Jackson
JSON-P (Processing) jakarta.json.* Low-level: manual parsing and building via JsonObject, JsonParser Jackson's JsonParser / JsonGenerator streaming API
JSON-B (Binding) jakarta.json.bind.* High-level: object ↔ JSON via Jsonb, mirrors Jackson's ObjectMapper Jackson's ObjectMapper

Both are valid specs, correctly designed, and implemented by libraries like Yasson (JSON-B) and Eclipse Parsson (JSON-P). They lost to Jackson in practice for three reasons: Jackson predated both specs and was already entrenched; Jackson's annotation model (@JsonProperty, @JsonIgnore) is richer and more flexible; and Spring Boot chose Jackson as its default HttpMessageConverter, which gave it the ecosystem momentum that compounds over time.

The one place where JSON-P still wins: streaming very large files

JSON-P's JsonParser is a true streaming parser β€” it reads a JSON document token by token without loading it into memory. For files measured in hundreds of MB or GB (log exports, data dumps), the streaming API avoids an OutOfMemoryError that Jackson's tree model would cause. Jackson also has a streaming API (JsonParser), but JSON-P's is the Jakarta standard and is supported natively on any Jakarta EE server without adding a dependency. For everything else in a Spring Boot project, Jackson is the right tool β€” covered in depth in Jackson β€” JSON in Java.

Jakarta EE Profiles β€” Full, Web, and Core

Jakarta EE is not monolithic β€” you don't have to use all specs. Profiles define which subset is required for certification:

ProfileIncludesTypical useExample server
Core Profile CDI Lite, JSON-B, JSON-P, REST Client (JAX-RS client) Microservices, cloud-native, serverless Helidon, Open Liberty Micro
Web Profile Core + Servlets, JPA, Bean Validation, EJB Lite, WebSocket Web applications, REST APIs β€” most real projects fit here TomEE, Payara Micro
Full Platform Web + JMS, full EJB, JAXB, Batch, Concurrency Large enterprise monoliths with messaging and batch processing WildFly, Payara Server
The Maven dependency you need varies by profile
<!-- Full Platform β€” compile against everything, server provides at runtime -->
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version>11.0.0</version>
    <scope>provided</scope>  <!-- server provides at runtime -->
</dependency>

<!-- Web Profile only β€” smaller compile-time surface -->
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-web-api</artifactId>
    <version>11.0.0</version>
    <scope>provided</scope>
</dependency>

The provided scope is critical β€” these APIs are provided by the application server at runtime. Including them with compile scope would package them into your WAR and cause class conflicts with what the server already ships.

Interview Questions

πŸŽ“ Junior level

Q: What is the difference between a specification and an implementation in the context of Jakarta EE?
A specification defines the API β€” interfaces, annotations, and contracts β€” without providing runnable code. An implementation is the library or server that actually executes against that API. JPA is a specification; Hibernate is its most common implementation. Servlet is a specification; Tomcat is its most common implementation.

Q: Why did Jakarta EE rename from javax.* to jakarta.* β€” what forced it?
Oracle retained the trademark on the javax namespace after donating the platform to the Eclipse Foundation. The Foundation could not evolve the APIs under javax.* without Oracle's explicit approval per change. To regain control of the platform's evolution, the namespace was renamed to jakarta.* β€” a breaking change that required every library, framework, and application to update its imports.

Q: What is a CDI Bean and how does it differ from a plain Java object?
A CDI bean is a Java object whose lifecycle is managed by the CDI container. The container creates it, injects its dependencies, calls @PostConstruct after injection is complete, and calls @PreDestroy before destroying it. A plain object created with new gets none of this β€” injection fields stay null, no lifecycle callbacks are invoked, and no interceptors apply.

πŸ”₯ Senior level

Q: A Spring Boot developer says "I don't need to know Jakarta EE." What's the precise technical counter-argument?
Every Spring Boot 3.x project uses Jakarta EE specs directly: jakarta.servlet.* for HTTP processing, jakarta.persistence.* for JPA entities, jakarta.validation.* for Bean Validation, and jakarta.transaction.* for transaction semantics. Spring doesn't replace these specs β€” it implements them or builds abstractions on top. Not understanding the spec/implementation boundary means not understanding what Spring Data JPA actually does (wraps EntityManager), why @Transactional works the way it does (JTA semantics), or why Quarkus annotations look familiar (it implements the same specs directly).

Q: Why does CDI inject a proxy instead of the real bean, and what constraints does that impose?
When a longer-lived bean (e.g. @ApplicationScoped) receives an injection of a shorter-lived bean (e.g. @RequestScoped), the real instance doesn't exist at injection time β€” it changes per request. CDI solves this with a generated client proxy: a subclass that intercepts every method call and forwards it to the correct real instance for the current context. Because it subclasses the bean, three constraints apply: the class cannot be final, none of its methods can be final, and it must have a no-arg constructor. A violation is a deployment error at startup, not a runtime NullPointerException.

Q: What is the difference between the Web Profile and the Core Profile, and when would you choose each?
The Core Profile (introduced in Jakarta EE 10) is the minimal subset β€” CDI Lite, JSON-B, JSON-P, and the JAX-RS client β€” designed for microservices that need to be small and start fast, targeting Quarkus and Helidon. The Web Profile adds Servlets, full JPA, Bean Validation, EJB Lite, and WebSocket β€” everything needed for a complete web application. Choose Core when building a stateless service or function that runs on a reactive runtime; choose Web Profile for anything that needs HTTP request handling, database persistence, or session management.