In Quarkus, a modern Java framework optimized for cloud-native and serverless applications, Contexts and Dependency Injection (CDI) is a core feature for managing beans. While dependency injection via @Inject
is the standard way to access beans, there are scenarios where you need to programmatically look up beans at runtime. This article explores various methods to programmatically look up beans in Quarkus, with practical examples and best practices.
Why would you need programmatic bean lookup?
You need to resolve beans dynamically based on runtime conditions (e.g., selecting a specific implementation).
You’re working in a non-CDI-managed class (e.g., a static context, utility class or third-party integrations).
You need to iterate over all beans of a given type or select beans with specific qualifiers.
You want to check for bean availability or defer bean resolution for performance reasons.
Quarkus provides several approaches to programmatically look up beans, leveraging its optimized CDI implementation, Arc
. The primary methods include using the Arc container, the standard CDI API, Instance<T>
with or without @Any
.
Methods for programmatic bean lookup
For all examples, we'll use the following bean interface and implementation:
public interface MyBean {
String getName();
void doSomething();
}
@ApplicationScoped
public class BeanImpl1 implements MyBean{
@Override
public String getName() {
return "Standard Bean";
}
@Override
public void doSomething() {
System.out.println("BeanImpl1 is working!");
}
}
- Using the Arc Container(Quarkus-specific)
The Arc container is Quarkus CDI implementation, optimized for fast startup and low memory usage. It provides a straightforward API for programmatic bean lookup via Arc.container()
.
InstanceHandle<MyBean> beanHandle = Arc.container().instance(MyBean.class);
if (beanHandle.isAvailable()) {
MyBean myBean = beanHandle.get();
myBean.doSomething();
} else {
System.out.println("MyBean not found");
}
However, when running this code, you might encounter the following error:
================================================================================
CDI: programmatic lookup problem detected
-----------------------------------------
At least one bean matched the required type and qualifiers but was marked as unused and removed during build
Stack frame:
Required type: interface org.acme.beans.MyBean
Required qualifiers: [@jakarta.enterprise.inject.Default()]
Removed beans:
- CLASS bean [types=[interface org.acme.beans.MyBean, class org.acme.beans.BeanImpl1], qualifiers=null]
Solutions:
- Application developers can eliminate false positives via the @Unremovable annotation
- Extensions can eliminate false positives via build items, e.g. using the UnremovableBeanBuildItem
- See also https://quarkus.io/guides/cdi-reference#remove_unused_beans
- Enable the DEBUG log level to see the full stack trace
================================================================================
Understanding the Problem
Quarkus uses build-time optimization to improve startup time and reduce memory usage. Part of this optimization is unused bean removal, where Quarkus analyzes your application at build time and removes beans that appear to be unused.
When using Arc.container().instance(MyBean.class)
for programmatic lookup, you're encountering this error because Quarkus couldn't detect at build time that BeanImpl1
(which implements MyBean) would be needed at runtime. The bean was considered "unused" and was removed during the build process, and when you try to look it up at runtime, it's no longer available.
Solutions to Fix the Problem
1.Mark Your Bean as @Unremovable
@Unremovable
@ApplicationScoped
public class BeanImpl1 implements MyBean {
// Implementation
}
2.Disable Unused Bean Removal (in application.properties)
quarkus.arc.remove-unused-beans=false
- Using the Standard CDI API
MyBean myBean = CDI.current().select(MyBean.class).get();
myBean.doSomething();
- Using
@Inject
withInstance<T>
For CDI-managed beans, using Instance<T>
offers a flexible approach to dynamic lookup.
@Inject
Instance<MyBean> myBeanInstance;
if (myBeanInstance.isResolvable()) {
MyBean bean = myBeanInstance.get();
bean.doSomething();
}
- Using
@Any
withInstance<T>
The @Any
qualifier extends Instance<T>
capabilities to access all beans of a type regardless of qualifiers, which is particularly useful for plugins or strategy patterns.
Let's create an additional implementation for our bean:
@ApplicationScoped
public class BeanImpl2 implements MyBean{
@Override
public String getName() {
return "Specific BeanImpl2";
}
@Override
public void doSomething() {
System.out.println("BeanImpl2 (specificBean) is working!");
}
}
Now we can look up all beans:
@Inject
@Any
Instance<MyBean> allBeans;
System.out.println("Found " + allBeans.stream().count() + "implementations");
allBeans.forEach(bean -> {
System.out.println("Processing bean: " + bean.getClass().getSimpleName());
bean.doSomething();
});
Output:
Found 2 implementations
Processing bean: BeanImpl1_ClientProxy
BeanImpl1 is working!
Processing bean: BeanImpl2_ClientProxy
BeanImpl2 (specificBean) is working!
Conclusion
Programmatic bean lookup in Quarkus provides flexibility for dynamic bean resolution and working with multiple implementations. While @Inject should remain your primary approach for accessing beans in most scenarios, the methods described in this article offer powerful alternatives for specific use cases.
When choosing a lookup method, consider:
- Build-time optimization impacts (remember the @Unremovable annotation when needed)
- Whether you need to access multiple beans or specific implementations
- The context in which you're performing the lookup (CDI-managed vs. non-CDI-managed.
Top comments (0)