ホームページ  >  記事  >  Java  >  Quarkus での合成 Bean の探索。強力な拡張メカニズム

Quarkus での合成 Bean の探索。強力な拡張メカニズム

PHPz
PHPzオリジナル
2024-08-30 06:02:32335ブラウズ

Exploring Synthetic Beans in Quarkus. A Powerful Extension Mechanism

Quarkus の世界では、依存関係注入の領域は豊富かつ多用途であり、開発者に Bean を管理および制御するための多数のツールを提供します。そのようなツールの 1 つは、合成 Bean の概念です。合成 Bean は、Java クラス、メソッド、またはフィールドから派生したものではない属性を持つ Bean を登録できる強力な拡張メカニズムです。代わりに、合成 Bean のすべての属性は拡張機能によって定義されます。

この記事では、Quarkus の合成 Bean の世界を深く掘り下げていきます。合成 Bean の必要性、その実用的なアプリケーション、Quarkus アプリケーションで合成 Bean を作成して使用する方法について探っていきます。

合成 Bean について理解する

Quarkus では、Bean はアプリケーションの構成要素であり、Contexts and dependency Injection (CDI) フレームワークによって管理されます。通常、CDI Bean は、@ApplicationScoped、@RequestScoped、または @Inject などのさまざまな CDI アノテーションが付けられた Java クラスです。これらの注釈
CDI が Bean のライフサイクルと注入を自動的に管理できるようにします。

ただし、従来の CDI モデルにうまく適合しない Bean を登録する必要がある状況もあります。ここで合成豆が活躍します。合成 Bean は拡張機能によって作成され、その属性はこれらの拡張機能によって完全に定義されます。通常の CDI の世界では、AfterBeanDiscovery.addBean() メソッドと SyntheticComponents.addBean() メソッドを使用してこれを実現します。 Quarkus では、これは SyntheticBeanBuildItem を使用して実現されます。

合成豆が必要になるのはいつですか?

では、Quarkus で合成 Bean を使用する必要があるのはどのような場合でしょうか?合成 Bean は、次の場合に強力なツールとなります。

  1. サードパーティ ライブラリの統合: CDI アノテーションを持たないサードパーティ ライブラリを使用していますが、CDI ベースのアプリケーションに統合する必要があります。合成豆を使用すると、このギャップを埋めることができます。

  2. 動的 Bean 登録: 構成またはその他の要因に応じて、実行時に Bean を動的に登録する必要があります。合成 Bean を使用すると、その場で Bean を作成して登録できる柔軟性が得られます。

  3. カスタマイズされた Bean 管理: 標準の CDI アノテーションでは実現できない、Bean のスコープと動作に対するきめ細かい制御が必要です。

  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 では、SyntheticBeanBuildItem クラスのおかげで、合成 Bean の作成は簡単なプロセスです。合成 Bean を作成して使用する手順を見てみましょう:

  1. 合成 Bean クラスの作成: まず、合成 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 拡張機能の作成: 合成 Bean を登録するには、Quarkus 拡張機能を作成する必要があります。この拡張クラスは、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 での合成 Bean の探索。強力な拡張メカニズムの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。