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

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

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

Quarkusのクラスローディングアーキテクチャは、アプリケーションを実行するモードによって若干異なります。

fast-jar パッケージタイプ (デフォルト) を使って本番アプリケーションを実行する場合、ほとんどすべての依存関係は、ビルド時にクラスをインデックスする io.quarkus.bootstrap.runner.RunnerClassLoader を介してロードされ、依存関係の小さなセットは、システムの ClassLoader からロードされます。

legacy-jar パッケージタイプを使用して本番アプリケーションを実行すると、すべてがシステム ClassLoader で読み込まれるため、完全にフラットなクラスパスとなります。

GraalVMは複数のClassLoaderを実際にサポートしていないため、フラットクラスパス戦略はGraalVMネイティブイメージにも使用されます。

その他のすべてのユースケース(テスト、開発モード、アプリケーションのビルドなど)では、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があります。これはそれらの関係を示しています:

Quarkus ClassLoader hierarchy

各ClassLoaderの役割は以下の通りです:

ベース ClassLoader

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

オーグメンテーション ClassLoader

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

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

デプロイメント ClassLoader

これはすべてのアプリケーション・クラスをロードできます。その親はオーグメンテーションクラスローダーなので、すべてのデプロイメントクラスをロードすることもできます。

この 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 のブートストラップ自体

必要であれば、quarkus-extension-maven-plugin で設定することができます。ある依存関係を親優先としてマークした場合、その依存関係もすべて親優先でなければならないことに注意してください。

<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</parentFirstArtifact>
            <parentFirstArtifact>org.jboss.logging:jboss-logging</parentFirstArtifact>
            <parentFirstArtifact>org.ow2.asm:asm</parentFirstArtifact>
        </parentFirstArtifacts>
    </configuration>
</plugin>

禁止された依存関係

依存関係の中には、確実に不要なものがあります。これは通常、依存関係の名前が変更されたときに起こります(例えば、smallrye-config が org.smallrye から org.smallrye.config にグループを変更したとき、javax から jakarta に名前を変更したときなどです)。 このようなアーティファクトが依存関係ツリーに残ってしまうと、Quarkusと互換性のない古いクラスがロードされる可能性があり、問題を引き起こす可能性があります。この問題に対処するために、エクステンションでは、決してロードされないアーティファクトを指定することができます。これは、pomの`quarkus-extension-maven-plugin`を変更することで行います(これにより、`quarkus-extension.properties`ファイルが生成されます)。以下のように、`excludedArtifacts`セクションを追加するだけです:

<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.

This config property can only be set in application.properties

Environment variable: QUARKUS_CLASS_LOADING_PARENT_FIRST_ARTIFACTS

Show more

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.

This config property can only be set in application.properties

Environment variable: QUARKUS_CLASS_LOADING_RELOADABLE_ARTIFACTS

Show more

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

Show more

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

Show more

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));
}

関連コンテンツ