CDI Full vs CDI Lite: What's new in Contexts and Dependency Injection 4.0

There’s an interesting change packaged inside the CDI 4.0 release that will change the way you think about the API.

Version 4.0 of Jakarta Contexts and Dependency Injection (CDI) changed the specification structure. Previously, the CDI spec included a CDI Core part, which was the basis for the CDI SE and CDI EE implementations.

But in 4.0, the CDI Core is restructured into two parts: CDI Full and CDI Lite. CDI Full provides the entire specification as well as a smaller subset of it, called CDI Lite.

The restructuring is a significant change at the implementation level, but developers who maintain existing applications won’t even notice the change. Apps that use the prior CDI specification are unaffected by how the Core CDI has been restructured.

If you’re a Jakarta EE developer, the CDI EE part of the specification — which is what Jakarta EE application servers implement — still requires the entire Core CDI. So for both enterprise and standard edition developers, the change will be completely transparent.

Difference between CDI and CDI Lite

The key difference between the full CDI and CDI Lite is that you can implement the lite spec in a build-time oriented manner.

CDI Lite contains the most useful features of CDI Full. It allows many dependency-injection frameworks to implement this highly functional subset of CDI Full.

AtInject, also known as Java Specification Request (JSR) 330, is a good example of how CDI Lite can be useful when a project requires build-time orientation.

Jakarta CDI 3.0 vs CDI 4.0 structural configuration

Runtime vs build-time injection

AtInject has very popular implementations of both runtime and build-time oriented architectures.

For example, Guice is a popular runtime implementation of AtInject, while Dagger is a popular build-time implementation that uses annotation processing. The most important aspects of a build-time oriented implementation in a dependency-injection framework are that it:

  • Precomputes all the dependency wiring
  • Pregenerates the support classes during application build

CDI Lite is carefully designed to support these build-time capabilities.

CDI Lite functionality

If it sounds like a big chunk of CDI functionality was removed, fear not. CDI Lite is a versatile framework, and it preserves all but a few Core CDI capabilities. Those removed features includes:

  • Session scope and conversation scope
  • Some parts of the BeanManager API
  • Per-bean archive enablement of decorators, interceptors, and alternatives
  • Interceptors bound using @Interceptors
  • Explicit bean archives, which equates to the bean discovery mode of all
  • @AroundInvoke interceptors declared on target classes
  • Portable extensions
  • Decorators, specialization and passivation

Core CDI and CDI Lite similarities

All other Core CDI functionality is part of CDI Lite, notably:

  • All normal scopes defined by CDI, except session and conversation, all pseudo-scopes defined by CDI, the corresponding contexts, and the corresponding lifecycle management
  • Implicit bean archives (bean discovery mode of annotated), as well as the option to designate an archive as not a bean archive (bean discovery mode of none)
  • @AroundInvoke interceptors declared on the interceptor class, as well as lifecycle callbacks defined on the target class and the interceptor class
  • Global enablement of interceptors and alternatives (using @Priority)
  • Synchronous and asynchronous events and observer methods
  • Interceptors bound using interceptor binding annotations
  • Everything related to basic dependency injection

The BeanManager API interface was also split up. There is a new BeanContainer interface in CDI Lite. Also, the BeanManager interface now extends BeanContainer to provide the remaining methods as part of CDI Full.

Replacing portable extensions in CDI Lite

The only significant feature missing in CDI Lite is the portable extensions mechanism. There are two main reasons for this:

  • The API is heavy on reflection, which is either costly or downright forbidden during application build.
  • The lifecycle of portable extensions is intrinsically intertwined with the lifecycle of a running CDI container.

Instead, CDI Lite comes with a new extension API called build-compatible extensions. This API doesn’t use reflection at all. Instead, it uses a new language model that can be implemented on top of reflection or other representations, such as javax.lang.model or Jandex. It also doesn’t require a running CDI container.

Because CDI Lite is a subset of CDI Full, build-compatible extensions are required to work in CDI Full environments. There’s also a mechanism for build-compatible extensions and portable extensions to coexist. Read this CDI blog post to learn more about build-compatible extensions.

For compatibility reasons, there’s still a single Java archive file that contains all the APIs, so you don’t have to worry that CDI Lite will rename packages. The Javadoc for the CDI types indicates whether they’re available in CDI Lite or CDI Full. The language model API is an exception. Some other Jakarta EE specifications expressed interest in this API, so that is shipped as a separate JAR file and may eventually be split out of CDI into another specification.

Quality of Life Improvements

Some relatively small changes in CDI 4.0 deliver other quality-of-life improvements.

CDI will finally include events for observing application lifecycle. Previously, developers had to use the @Initialized(ApplicationScoped.class) Object and @BeforeDestroyed(ApplicationScoped.class) Object events, but these correspond to the lifecycle of the CDI application context rather than to the application lifecycle.

That doesn’t sound like a huge difference, but certain build-time oriented implementations initialize the application context in a completely different process long before the application starts. The addition of Startup and Shutdown events enable developers to can use them to run code on application startup and shutdown.

If you ever needed an automated process to look up beans, you’ve probably come across the Instance API. It lets you iterate over beans of a certain type with certain qualifiers, but it also instantiates all beans which isn’t always desirable. The Instance Handle API allows you to iterate inspections of beans’ metadata without instantiation. As a result, programmatic bean lookups should no longer require access to the BeanManager.

Finally, in CDI 4.0 developers can apply the @Priority annotation on stereotypes. This enables useful functions, such as the ability to declare a common @Mock stereotype, which would be a globally enabled @Alternative.

Breaking changes in CDI 4.0

There are some breaking changes too. For example, CDI 4.0 removes several deprecated APIs:

  • @New qualifier
  • addAnnotatedType(AnnotatedType) method
  • Some methods on the BeanManager interface

The most important breaking change is how CDI 4.0 treats empty beans.xml files, which were marked an explicit bean archive. Now, empty beans.xml files are marked as an implicit bean archive, which changes the bean discovery mode from all to annotated. All products that implement CDI Full must provide a switch to the previous behavior.

CDI 4.0 and Weld

Weld remains a compatible implementation in CDI 4.0, and a beta release is already available. It also implements the build-compatible extension API, using translation to portable extensions. The Eclipse Open DI project, which is based on Micronaut, is currently working on a CDI Lite-compliant, build-time oriented implementation. ArC, a subproject of Quarkus, is also working to become CDI Lite-compliant.

Ladislav Thon works for Red Hat in Czech Republic, is a contributor to MicroProfile, and a committer to the Jakarta CDI specification.

App Architecture
Software Quality
Cloud Computing