What's new in CDI 4.1?
27 Feb 2024 - Ladislav Thon
If you’re following the activity on the CDI repository, or in Jakarta EE in general, you might have noticed that the CDI 4.1 release is coming soon. In this short post, we’ll take a look at what’s new in the wonderful world of CDI.
Method Invokers
The most important (or at least the biggest) addition to CDI is method invokers. It is not terribly interesting for application developers, but it should be very useful for people who build CDI-based frameworks.
A method invoker is, unsurprisingly, an object that allows invoking a method indirectly.
It implements the jakarta.enterprise.invoke.Invoker
interface, which is very simple:
interface Invoker<T, R> {
R invoke(T instance, Object[] arguments) throws Exception;
}
CDI extensions can create an invoker for methods (including static
methods) declared on managed beans.
For example, let’s say the application contains this bean:
@Dependent
public class MyService {
public String hello(String name) {
return "Hello " + name + "!";
}
}
A hypothetical CDI-based framework can decide that it might need to invoke the method at runtime. During deployment, the CDI extension provided by the framework would use
ProcessManagedBean.createInvoker()
, if using a Portable ExtensionInvokerFactory.createInvoker()
, if using a Build Compatible Extension
to obtain an InvokerBuilder
. Calling build()
on it will produce
- an
Invoker
directly, if using a Portable Extension - an
InvokerInfo
that materializes as anInvoker
at application runtime, if using a Build Compatible Extension
The Invoker
then needs to be stored somewhere until the framework actually needs it.
When the time comes to invoke the method, the framework takes the Invoker
, obtains an instance of MyService
and all the parameter values, and calls
invoker.invoke(myService, new Object[] { "world" })
The return value is "Hello world!"
, as expected.
Lookups
It is fairly common that some of the parameters the method accepts are CDI beans.
It is possible to configure the InvokerBuilder
to resolve and instantiate a correct value for such parameter automatically.
In fact, this is possible also for the instance on which the method should be invoked.
The InvokerBuilder
has methods withInstanceLookup()
and withArgumentLookup()
to configure that.
Returning back to the example above, if the CDI extension called withInstanceLookup()
before calling InvokerBuilder.build()
, it wouldn’t have to obtain the instance of MyService
manually.
The invoker would do it automatically.
The invocation would just use null
as the instance value:
invoker.invoke(null, new Object[] { "world" })
The result is still the same: "Hello world!"
.
Other Improvements
There’s a couple of quality of life improvements in this release, too.
Interceptors can now access the interceptor binding annotations from InvocationContext
, using methods getInterceptorBinding()
and getInteceptorBindings()
.
This is technically a change in the Interceptors specification, but is most useful for CDI programmers, so we mention it here as well.
Producer methods and fields can now declare a @Priority
directly (or obtain a priority from a stereotype); they no longer have to rely on the priority value of their declaring bean.
When both the producer and its declaring bean have a priority, the producer’s priority wins.
The newly added methods BeanContainer.isMatchingBean()
and isMatchingEvent()
provide programmatic access to the typesafe resolution and observer resolution rules.
This is useful for frameworks that want to perform the same matching as CDI, but don’t want to reimplement the rules themselves.
The newly added method BeanContainer.getContexts()
returns context objects of all registered contexts for given scope, regardless of whether they are active or inactive.
This is useful for authors of Build Compatible Extensions, who don’t provide a context object to the container directly.
Instead, they provide a class of the context object, and they need to use this method to gain access to the context object which the CDI container creates internally.
Deprecations
CDI 4.1 prepares for removing a dependency on Unified EL from the core CDI API (cdi-api.jar
).
This dependency is very narrow and really only useful for application server authors, but it’s still there.
Specifically, there are 2 methods on the BeanManager
interface that use EL types in their signatures:
getELResolver()
, which returnsELResolver
wrapExpressionFactory()
, which accepts and returnsExpressionFactory
These 2 methods are deprecated in CDI 4.1 and shall be removed in CDI 5.0.
The suggested replacement is the ELAwareBeanManager
interface, which extends BeanManager
and contains the same 2 methods, but exists outside of the core CDI API, in a supplemental CDI EL API artifact (cdi-el-api.jar
).
As mentioned above, these methods are mainly used by application servers, so this shouldn’t really affect CDI users. Also, CDI users don’t have to consume the new supplemental artifact; compiling against the existing CDI API is fine.
CDI EE Specification Moved
This is more of a curiosity from user perspective, but relatively important change internally.
The CDI in Jakarta EE part of the CDI specification, or CDI EE for short, was moved from the CDI specification to the Jakarta EE Platform specification.
Together with that, the corresponding part of the CDI TCK (the web
module, basically) was moved to the Jakarta EE Platform TCK.
This doesn’t affect CDI users, but is important for CDI implementations.
Conclusion
CDI 4.1 is not a huge release, but it contains improvements useful for both application developers and framework authors.
If you have any questions, the friendly mailing list cdi-dev@eclipse.org
is a great place to ask!