首页  >  文章  >  Java  >  探索 Quarkus 中的合成豆。强大的扩展机制

探索 Quarkus 中的合成豆。强大的扩展机制

PHPz
PHPz原创
2024-08-30 06:02:32335浏览

Exploring Synthetic Beans in Quarkus. A Powerful Extension Mechanism

在 Quarkus 的世界中,依赖注入领域丰富且用途广泛,为开发人员提供了大量管理和控制 Bean 的工具。其中一种工具是合成豆的概念。合成 bean 是一种强大的扩展机制,允许您注册其属性不是从 Java 类、方法或字段派生的 bean。相反,合成 bean 的所有属性都由扩展定义。

在本文中,我们将深入了解 Quarkus 中的合成豆世界。我们将探讨合成 bean 的需求、它们的实际应用,以及如何在 Quarkus 应用程序中创建和使用它们。

了解合成豆

在 Quarkus 中,bean 是应用程序的构建块,由上下文和依赖注入 (CDI) 框架管理。通常,CDI bean 是使用各种 CDI 注释(例如 @ApplicationScoped、@RequestScoped 或 @Inject)进行注释的 Java 类。这些注释
允许 CDI 自动管理 Bean 的生命周期和注入。

但是,在某些情况下,您可能需要注册一个不太适合传统 CDI 模型的 Bean。这就是合成豆发挥作用的地方。合成 bean 由扩展创建,并且其属性完全由这些扩展定义。在常规 CDI 的世界中,您可以使用 AfterBeanDiscovery.addBean() 和 SyntheticComponents.addBean() 方法来实现此目的。在 Quarkus 中,这是使用 SyntheticBeanBuildItem 完成的。

什么时候需要合成豆?

那么,什么时候您可能需要在 Quarkus 中使用合成 Bean?合成豆在以下情况下是一个强大的工具:

  1. 集成第三方库:您正在使用没有 CDI 注释但需要集成到基于 CDI 的应用程序中的第三方库。合成豆可以帮助您弥补这一差距。

  2. 动态 Bean 注册: 您需要在运行时动态注册 Bean,具体取决于配置或其他因素。合成 Bean 使您可以灵活地动态创建和注册 Bean。

  3. 自定义 Bean 管理:您需要对 Bean 的范围和行为进行细粒度控制,这是标准 CDI 注释无法实现的。

  4. 实现专用 Bean:您想要创建具有与传统 Java 类或方法不对应的独特属性的专用 Bean。

  5. 模拟依赖项以进行测试:合成 bean 提供了一种有用的方法来模拟依赖项并注入模拟实现以进行测试。

综合完成构建项

SynthesisFinishedBuildItem 用于指示 CDI bean 发现和注册过程已完成。这允许扩展知道何时可以安全地与已注册的 Bean 进行交互。

例如:

@BuildStep  
void onSynthesisFinished(SynthesisFinishedBuildItem synthesisFinished){
    // CDI bean registration is complete, can now safely interact with beans
    }

SyntheticBeansRuntimeInitBuildItem

SyntheticBeansRuntimeInitBuildItem 用于注册一个回调,该回调将在所有合成 bean 初始化后在运行时调用。如果您需要执行涉及合成 bean 的额外初始化逻辑,这非常有用。

例如:

@BuildStep
SyntheticBeansRuntimeInitBuildItem initSyntheticBeans(){

    return new SyntheticBeansRuntimeInitBuildItem(ids->{
    // Perform logic with initialized synthetic beans
    });

    }

传递给 SyntheticBeansRuntimeInitBuildItem 的回调将收到一个 Set包含所有初始化的合成 bean 的 ID。

总而言之,SynthesisFinishedBuildItem 表示 Bean 发现已完成,而 SyntheticBeansRuntimeInitBuildItem 允许根据合成 Bean 初始化逻辑。

使用 SyntheticBeanBuildItem 创建合成 Bean

在 Quarkus 中,创建合成 bean 是一个简单的过程,这要归功于 SyntheticBeanBuildItem 类。让我们逐步完成创建和使用合成 bean 的步骤:

  1. 创建合成 Bean 类: 首先定义合成 Bean 类。该类将成为您的合成豆的基础。
package com.iqnev;

public class MySyntheticBean {

  // Define the behavior and attributes of your synthetic bean
  public void printMessage() {
    System.out.println("Hello from synthetic bean!");
  }
}
  1. 创建 Quarkus 扩展: 您需要创建 Quarkus 扩展来注册您的合成 bean。该扩展类将使用 SyntheticBeanBuildItem 来配置您的 bean。

字节码生成方法

package com.iqnev;

import io.quarkus.arc.deployment.SyntheticBeanBuildItem;

public class MySyntheticBeanExtension {

  @BuildStep
  SyntheticBeanBuildItem syntheticBean() {
    return SyntheticBeanBuildItem
        .configure(MySyntheticBean.class)
        .scope(ApplicationScoped.class)
        .creator(mc -> {
          mc.returnValue(new MySyntheticBean());
        })
        .done();
  }
}

SyntheticBeanBuildItem 上的 .creator() 方法用于生成字节码,该字节码将在运行时创建合成 bean 的实例。

传递给 .creator() 的参数是 Consumer;它允许在方法内生成 Java 字节码。

在此示例中:

  1. mc is the MethodCreator instance
  2. mc.returnValue(new MySyntheticBean()) generates the bytecode to create a new instance of MySyntheticBean and return it from the method.

So essentially, we are telling Quarkus to generate a method that looks something like:

MySyntheticBean createSyntheticBean(){
    return new MySyntheticBean();
    }

This generated method will then be called to instantiate the MySyntheticBean when it needs to be injected or used.

The reason bytecode generation is used is that synthetic beans do not correspond to real Java classes/methods, so we have to explicitly generate a method to instantiate them

The output of SyntheticBeanBuildItem is bytecode recorded at build time. This limits how instances are created at runtime. Common options are:

  1. Generate bytecode directly via .creator()
  2. Use a BeanCreator subclass
  3. Produce instance via @Recorder method

Recorder Approach

The @Record and .runtimeValue() approaches are alternate ways of providing instances for synthetic beans in Quarkus.

This allows you to instantiate the synthetic bean via a recorder class method annotated with @Record(STATIC_INIT).

For example:

@Recorder
public class MyRecorder {

  @Record(STATIC_INIT)
  public MySyntheticBean createBean() {
    return new MySyntheticBean();
  }

}

  @BuildStep
  SyntheticBeanBuildItem syntheticBean(MyRecorder recorder) {
    return SyntheticBeanBuildItem
        .configure(MySyntheticBean.class)
        .runtimeValue(recorder.createBean());
  }

Here the .runtimeValue() references the recorder method to instantiate the bean. This allows passing a RuntimeValue directly to provide the synthetic bean instance.

For example:

@BuildStep 
SyntheticBeanBuildItem syntheticBean(){

    RuntimeValue<MySyntheticBean> bean= //...

    return SyntheticBeanBuildItem
    .configure(MySyntheticBean.class)
    .runtimeValue(bean);

    }

The RuntimeValue could come from a recorder, supplier, proxy etc.

So in summary:

  • @Record is one approach to generate the RuntimeValue
  • .runtimeValue() sets the RuntimeValue on the SyntheticBeanBuildItem

They both achieve the same goal of providing a runtime instance, just in slightly different ways.

When it comes to providing runtime instances for synthetic beans in Quarkus, I would consider using recorders (via @Record) to be a more advanced approach compared to directly generating bytecode
with .creator() or supplying simple RuntimeValues.

Here are some reasons why using recorders can be more advanced:

  • More encapsulation - The logic to instantiate beans is contained in a separate recorder class rather than directly in build steps. This keeps build steps lean.
  • Reuse - Recorder methods can be reused across multiple synthetic beans rather than rewriting creator logic.
  • Runtime data - Recorder methods execute at runtime so they can leverage runtime resources, configs, services etc. to construct beans.
  • Dependency injection - Recorder methods can inject other services.
  • Life cycle control - Recorder methods annotated with @Record(STATIC_INIT) or @Record(RUNTIME_INIT) give more control over bean instantiation life cycle.
  • Managed beans - Beans instantiated inside recorders can themselves be CDI managed beans.

So in summary, recorder methods provide more encapsulation, flexibility and access to runtime data and services for instantiating synthetic beans. They allow for more advanced bean production logic compared to direct bytecode generation.

However, direct bytecode generation with .creator() can still be useful for simple cases where recorders may be overkill. But as synthetic bean needs grow, recorders are a more powerful and
advanced approach.

It is possible to configure a synthetic bean in Quarkus to be initialized during the RUNTIME_INIT phase instead of the default STATIC_INIT phase.

Here is an example:

@BuildStep
@Record(RUNTIME_INIT)
SyntheticBeanBuildItem lazyBean(BeanRecorder recorder){

    return SyntheticBeanBuildItem
    .configure(MyLazyBean.class)
    .setRuntimeInit() // initialize during RUNTIME_INIT
    .runtimeValue(recorder.createLazyBean());

    }

The key points are:

  • Use setRuntimeInit() on the SyntheticBeanBuildItem to mark it for RUNTIME_INIT
  • The recorder method must be annotated with @Record(RUNTIME_INIT)
  • The runtime init synthetic beans cannot be accessed during STATIC_INIT

So in summary, synthetic beans can be initialized lazily during RUNTIME_INIT for cases where eager STATIC_INIT instantiation is not needed. This allows optimizing startup time.

Use the Synthetic Bean: Now that your synthetic bean is registered, you can inject and use it in your application.

package com.iqnev;

import javax.inject.Inject;

public class MyBeanUser {

  @Inject
  MySyntheticBean mySyntheticBean;

  public void useSyntheticBean() {
    // Use the synthetic bean in your code
    mySyntheticBean.printMessage();
  }
}

Running Your Application: Build and run your Quarkus application as usual, and the synthetic bean will be available for injection and use.

Conclusion

Synthetic beans in Quarkus provide a powerful mechanism for integrating external libraries, dynamically registering beans, and customizing bean behavior in your CDI-based applications. These beans, whose attributes are defined by extensions rather than Java classes, offer flexibility and versatility in managing dependencies.

正如我们在本文中探讨的那样,在 Quarkus 中创建和使用合成 bean 是一个简单的过程。通过利用 SyntheticBeanBuildItem 和 Quarkus 扩展,您可以无缝弥合传统 CDI 与更专业或动态 bean 注册要求之间的差距。

在不断发展的 Java 框架领域,Quarkus 通过提供合成 Bean 等创新解决方案继续脱颖而出,使其成为现代、高效和灵活的应用程序开发的引人注目的选择。拥抱 Quarkus 中合成 bean 的强大功能,将您的依赖注入提升到一个新的水平!

以上是探索 Quarkus 中的合成豆。强大的扩展机制的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn