How to programmatically look up a bean in Quarkus
Ivelin Yanev

Ivelin Yanev @yanev

About: #Java #Go #Qt #Quarkus

Location:
Sofia, Bulgaria
Joined:
Aug 23, 2024

How to programmatically look up a bean in Quarkus

Publish Date: May 15
3 0

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!");
  }
}
Enter fullscreen mode Exit fullscreen mode
  • 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");
    }
Enter fullscreen mode Exit fullscreen mode

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
================================================================================
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
  • Using @Inject with Instance<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();
    }
Enter fullscreen mode Exit fullscreen mode
  • Using @Any with Instance<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!");
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
    });
Enter fullscreen mode Exit fullscreen mode

Output:

Found 2 implementations
Processing bean: BeanImpl1_ClientProxy
BeanImpl1 is working!
Processing bean: BeanImpl2_ClientProxy
BeanImpl2 (specificBean) is working!
Enter fullscreen mode Exit fullscreen mode

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.

Comments 0 total

    Add comment