The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

クラスローディングリファレンス

このドキュメントでは、Quarkus のクラスロードアーキテクチャについて説明します。このドキュメントは、Quarkus がどのように動作するかを正確に理解したいエクステンションの作者や上級ユーザーを対象としています。

Quarkus のクラスロードアーキテクチャは、アプリケーションを実行するモードによって若干異なります。本番環境のアプリケーションを実行しているときは、すべてがシステム ClassLoader でロードされるため、完全にフラットなクラスパスとなります。これは、複数の ClassLoader をサポートしていないネイティブイメージモードにも当てはまり、通常のプロダクション Quarkus アプリケーションをベースにしています。

その他のすべてのユースケース(テスト、開発モード、アプリケーションのビルドなど)では、Quarkus はここで説明したクラスロードアーキテクチャを使用します。

Quarkus のブートストラップ

すべての Quarkus アプリケーションは、 independent-projects/bootstrap モジュールの QuarkusBootstrap クラスによって作成されます。このクラスは、Quarkus アプリケーションに必要なすべての関連する依存関係(デプロイメントとランタイムの両方)を解決するために使用されます。このプロセスの最終結果は、 CuratedApplication であり、これにはアプリケーションのすべてのクラスロード情報が含まれています。

次に、 CuratedApplication を使用して AugmentAction インスタンスを作成し、本番アプリケーションを作成したり、ランタイムのものを起動/再起動したりすることができます。このアプリケーションインスタンスは、分離された ClassLoader 内に存在しています。収集処理によって解決されるため、クラスパスに Quarkus デプロイメントクラスを配置する必要はありません。

このブートストラッププロセスは、Quarkus がどのように起動されても、異なるパラメーターが渡されているだけで、同じであるべきです。

現在の実行モード

現在のところ、Quarkus のブートストラップには以下のようなユースケースがあります。

  • Maven による本番アプリケーションの作成

  • Maven の開発モード

  • Gradle による本番アプリケーションの作成

  • Gradle の開発モード

  • QuarkusTest (Maven、Gradle、IDE)

  • QuarkusUnitTest (Maven、Gradle、IDE)

  • QuarkusDevModeTest(Maven、Gradle、IDE)

  • Arquillian アダプター

このリファクタの目的の一つは、これらの異なる実行モードのすべてが基本的に同じ方法で Quarkus を起動できるようにすることです。

Transformer の安全性に関する注意事項

ClassLoader は、Transformer が準備される前にクラスをロードしても安全であれば、「Transformerセーフ」と言われます。一度ロードされたクラスは変更することができないので、もし Transformer の準備が整う前にロードされた場合、変換が機能しなくなります。Transformer セーフな ClassLoader でクラスをロードした場合は、ロードされたクラスは実行時には使用されないので、変換を妨げることはありません。

クラスローダーの実装

Quarkus には以下のClassLoadersがあります:

ベース ClassLoader

これは通常、通常の JVM System ClassLoader です。Maven のような環境では異なる場合があります。この ClassLoader はブートストラップクラスをロードするために使用され、他の ClassLoader インスタンスは JDK クラスのロードをこれに委譲します。

拡張 ClassLoader

これは、すべての -deployment アーティファクトとその依存関係、および他のユーザー依存関係をロードします。アプリケーションのルートやホットデプロイされたコードはロードしません。この ClassLoader は永続的に動作し、アプリケーションが再起動してもそれは残ります (ホットデプロイされている可能性のあるアプリケーションクラスをロードできないのはそのためです)。親はベースの ClassLoader であり、Transformerセーフです。

現在のところ、これはベース ClassLoader に委譲するように設定することができますが、計画ではこのオプションは廃止され、常に分離された ClassLoader として使用することになっています。これを分離型 ClassLoader にすることは、すべてのビルダクラスが分離されていることを意味するので複雑です。

デプロイメント ClassLoader

This can load all application classes. Its parent is the Augment ClassLoader, so it can also load all deployment classes.

この ClassLoader は永続的ではなく、アプリケーションの起動時に再作成され、分離されています。この ClassLoader は、ビルドステップを実行する際に使用されるコンテキスト ClassLoader です。また、Transformerセーフでもあります。

ベースランタイム ClassLoader

これは、他のユーザー依存関係と同様に、すべての実行時エクステンション依存関係をロードします(これには 拡張 ClassLoader によってロードされたクラスの重複コピーも含まれる可能性があることに注意してください)。アプリケーションのルートやホットデプロイされたコードはロードされません。この ClassLoader は永続的なもので、アプリケーションが再起動しても残ります (ホットデプロイされている可能性のあるアプリケーションクラスをロードできないのはそのためです)。親はベース ClassLoader です。

これはホットリロードできないコードをロードしますが、変換をサポートしています (ただし、クラスがロードされるとこの変換はできなくなります)。これは、最初のアプリケーション起動時に登録された Transformer のみが有効になることを意味しますが、これらの Transformer は冪等であることが期待されているため、これは問題を引き起こすことはないはずです。ここで必要となる可能性のある変換の例として、外部 jar にパッケージ化された Panache エンティティーがあります。このクラスは静的メソッドを実装するために変換する必要がありますが、この変換は一度しか行われないため、再起動時には最初の起動時に作成されたクラスのコピーを使用します。

この ClassLoader は、拡張 ClassLoader とデプロイメント ClassLoader から分離されています。つまり、デプロイメント側の静的フィールドに値を設定して、実行時に読み込まれることを期待することはできません。これにより、開発アプリケーションやテストアプリケーションが本番アプリケーションのように振る舞うことができます(本番アプリケーションは全く新しい JVM で実行されるという点で分離されています)。

これは、実行時のバージョンは別の依存関係のセットに対してリンクされる可能性があることを意味します。例えば、デプロイ時に使用する Hibernate バージョンには ByteBuddy が含まれていても、実行時に使用するバージョンには含まれていない場合があります。

ランタイムクラス ローダー

この ClassLoader は、アプリケーションクラスやその他のホットデプロイ可能なリソースをロードするために使用されます。親はベースランタイム ClassLoader で、アプリケーションの再起動時に再作成されます。

分離型クラスローダー

ランタイムの ClassLoader は常に分離されています。これは、解決された依存関係リストのほとんどすべてのクラスの独自のコピーを持つことを意味します。これには例外があります:

  • JDK クラス

  • エクステンションが親優先とマークしたアーティファクトからのクラス (これについては後述します)。

親優先の依存関係

分離された方法でロードされるべきではないクラスがいくつかありますが、それは常にシステムの ClassLoader(または Quarkus のブートストラップを担当している ClassLoader )によってロードされなければなりません。ほとんどのエクステンションはこのことを気にする必要はありませんが、必要な場合もあります:

  • ロギングはシステム ClassLoader によってロードされなければならないので、いくつかのロギング関連のクラス

  • Quarkus のブートストラップ自体

If this is required it can be configured in the quarkus-extension-maven-plugin. Note that if you mark a dependency as parent first then all of its dependencies must also be parent first, or a LinkageError can occur.

<plugin>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-extension-maven-plugin</artifactId>
    <configuration>
        <parentFirstArtifacts>
            <parentFirstArtifact>io.quarkus:quarkus-bootstrap-core</parentFirstArtifact>
            <parentFirstArtifact>io.quarkus:quarkus-development-mode-spi</parentFirstArtifact>
            <parentFirstArtifact>org.jboss.logmanager:jboss-logmanager-embedded</parentFirstArtifact>
            <parentFirstArtifact>org.jboss.logging:jboss-logging</parentFirstArtifact>
            <parentFirstArtifact>org.ow2.asm:asm</parentFirstArtifact>
        </parentFirstArtifacts>
    </configuration>
</plugin>

禁止された依存関係

There are some dependencies that we can be sure we do not want. This generally happens when a dependency has had a name change (e.g. smallrye-config changing groups from org.smallrye to org.smallrye.config, the javaxjakarta rename). This can cause problems, as if these artifacts end up in the dependency tree out of date classes can be loaded that are not compatible with Quarkus. To deal with this, extensions can specify artifacts that should never be loaded. This is done by modifying the quarkus-extension-maven-plugin config in the pom (which generates the quarkus-extension.properties file). Simply add an excludedArtifacts section as shown below:

<plugin>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-extension-maven-plugin</artifactId>
    <configuration>
        <excludedArtifacts>
            <excludedArtifact>io.smallrye:smallrye-config</excludedArtifact>
            <excludedArtifact>javax.enterprise:cdi-api</excludedArtifact>
        </excludedArtifacts>
    </configuration>
</plugin>

これは、エクステンションがこれらのアーティファクトの新しいバージョンに依存している場合にのみ行うべきです。もしエクステンションが代替アーティファクトを依存関係として持ち込まなかった場合、 アプリケーションが必要とするクラスが見つからなくなってしまうかもしれません。

クラスローディングの設定

開発モードとテストモードでクラスローディングのいくつかの側面を設定することができます。これは、 application.properties を使用して行うことができます。クラスローディングの設定は通常の設定とは異なり、標準のQuarkusの設定メカニズムを使用していないため(必要な時期が早すぎるため)、 application.properties のみをサポートしていることに注意してください。以下のオプションがサポートされています。

ビルド時に固定される設定プロパティ - それ以外の設定プロパティは実行時に上書き可能

Configuration property

タイプ

デフォルト

Artifacts that are loaded in a parent first manner. This can be used to work around issues where a given class needs to be loaded by the system ClassLoader. Note that if you make a library parent first all its dependencies should generally also be parent first. Artifacts should be configured as a comma separated list of artifact ids, with the group, artifact-id and optional classifier separated by a colon. WARNING: This config property can only be set in application.properties

Environment variable: QUARKUS_CLASS_LOADING_PARENT_FIRST_ARTIFACTS

list of string

Artifacts that are loaded in the runtime ClassLoader in dev mode, so they will be dropped and recreated on change. This is an advanced option, it should only be used if you have a problem with libraries holding stale state between reloads. Note that if you use this any library that depends on the listed libraries will also need to be reloadable. This setting has no impact on production builds. Artifacts should be configured as a comma separated list of artifact ids, with the group, artifact-id and optional classifier separated by a colon. WARNING: This config property can only be set in application.properties

Environment variable: QUARKUS_CLASS_LOADING_RELOADABLE_ARTIFACTS

string

Artifacts that will never be loaded by the class loader, and will not be packed into the final application. This allows you to explicitly remove artifacts from your application even though they may be present on the class path.

Environment variable: QUARKUS_CLASS_LOADING_REMOVED_ARTIFACTS

list of string

Resources that should be removed/hidden from dependencies. This allows for classes and other resources to be removed from dependencies, so they are not accessible to the application. This is a map of artifact id (in the form group:artifact) to a list of resources to be removed. When running in dev and test mode these resources are hidden from the ClassLoader, when running in production mode these files are removed from the jars that contain them. Note that if you want to remove a class you need to specify the class file name. e.g. to remove com.acme.Foo you would specify com/acme/Foo.class. Note that for technical reasons this is not supported when running with JBang.

Environment variable: QUARKUS_CLASS_LOADING_REMOVED_RESOURCES

Map<String,Set<String>>

クラスやリソースを依存関係から隠す/取り除く

クラスやリソースを依存関係から隠したり、削除したりすることができます。これは上級者向けのオプションですが、ときには便利なこともあります。これは、 quarkus.class-loading.removed-resources 設定キーで行うことが可能です。例:

quarkus.class-loading.removed-resources."io.quarkus\:quarkus-integration-test-shared-library"=io/quarkus/it/shared/RemovedResource.class

これにより、 io.quarkus:quarkus-integration-test-shared-library のアーティファクトから RemovedResource.class のファイルが削除されます。

このオプションはクラスローディングオプションですが、生成されたアプリケーションにも影響するため、アプリケーションが作成されると、削除されたリソースにはアクセスできなくなります。

クラスバイトコードの読み取り

正しい ClassLoader を使用することが重要です。推奨される方法は、 Thread.currentThread().getContextClassLoader() メソッドを呼び出して取得することです。

例:

@BuildStep
GeneratedClassBuildItem instrument(final CombinedIndexBuildItem index) {
    final String classname = "com.example.SomeClass";
    final ClassLoader cl = Thread.currentThread().getContextClassLoader();
    final byte[] originalBytecode = IoUtil.readClassAsBytes(cl, classname);
    final byte[] enhancedBytecode = ... // class instrumentation from originalBytecode
    return new GeneratedClassBuildItem(true, classname, enhancedBytecode));
}