事前の最適化

この章では、Spring の事前最適化を基盤とした Spring Data の Ahead of Time (AOT) 最適化について説明します。

ベストプラクティス

ドメイン型にアノテーションを付ける

アプリケーションの起動時に、Spring はエンティティの初期処理のためにクラスパスをスキャンし、ドメインクラスを探します。ドメイン型に Spring Data ストア固有の @Table@Document@Entity アノテーションを付与することで、初期のエンティティスキャンを容易にし、それらの型がランタイムヒントの ManagedTypes に登録されるようにすることができます。ネイティブイメージ配置ではクラスパススキャンが不可能なため、Spring は初期エンティティセットに ManagedTypes を使用する必要があります。

事前コード生成

事前コード生成は、GraalVM Native Image での使用に限定されず、通常の デプロイで作業する場合にも利点があり、JVM での起動パフォーマンスの最適化に役立ちます。

AOT 最適化により、一部の決定 (たとえば、データベースダイアレクトなど) はビルド時に固定され、そのままアプリケーションセットアップに組み込まれます。

Ahead of Time コンパイルが有効になっている場合、Spring Data は (実際に使用されているモジュールに応じて) ビルドの AOT フェーズ中にいくつかのコンポーネントを提供できます。

  • 生成された型 / プロパティアクセサーのバイトコード

  • 定義されたリポジトリインターフェースのソースコード

  • JSON 形式のリポジトリメタデータ

上記の各機能はデフォルトで有効になっています。ただし、ユーザーは以下のオプションを使用して設定を微調整できます。

spring.aot.data.accessors.enabled

生成された型 / プロパティアクセサーのバイトコードのコントリビュートを制御するブールフラグ

spring.aot.data.accessors.include

生成された型 / プロパティアクセサーのバイトコードを提供する FQCN のカンマ区切りリスト。パッケージ名(例: example.springdata.**)または型名の包含に一致する Ant スタイルの include パターン。包含と除外の両方に一致する型の場合、包含が優先され、その型は包含されているとみなされます。

spring.aot.data.accessors.exclude

生成された型 / プロパティアクセサーのバイトコード提供をスキップする FQCN のカンマ区切りリスト。パッケージ名(例: example.springdata.**)または型名の除外に一致する Ant スタイルの除外パターン。包含と除外の両方に一致する型の場合、包含が優先され、その型は包含されているとみなされます。

spring.aot.repositories.enabled

リポジトリインターフェースのソースコードのコントリビュートを制御するブールフラグ

spring.aot.[module-name].repositories.enabled

特定のモジュールのリポジトリインターフェースのソースコードのコントリビュートを制御するブールフラグ (例: cassandrajdbcjpamongodb)

spring.aot.repositories.metadata.enabled

クエリメソッドと実際のクエリ文字列を含む JSON リポジトリメタデータの提供を制御するブールフラグ。spring.aot.repositories.enabled を有効にする必要があります。

事前リポジトリ

Ahead of Time リポジトリは、特定のモジュールの命令型(非リアクティブ)リポジトリインターフェースでのみ利用可能です。利用可能なクエリメソッドを特定する条件は、実装モジュールによって異なります。

AOT リポジトリは、適切なクエリメソッド実装を事前に生成することで AOT 処理を拡張するものです。クエリメソッドは、その呼び出しで実行されるクエリについて開発者には不透明です。AOT リポジトリは、ビルド時に既知の派生クエリ、アノテーション付きクエリ、名前付きクエリに基づいて、クエリメソッド実装を提供します。この最適化により、クエリメソッド処理が実行時からビルド時へと移行されます。これにより、アプリケーションの起動時にクエリメソッドをリフレクション的に分析する必要がなくなり、パフォーマンスが大幅に向上します。

結果として得られる AOT リポジトリフラグメントは、<Repository FQCN>Impl__AotRepository の命名スキームに従い、リポジトリインターフェースと同じパッケージに配置されます。

AOT リポジトリクラスは内部最適化を目的としています。生成と実装の詳細は将来のリリースで変更される可能性があるため、コード内で直接使用しないでください。

リポジトリメタデータ

AOT 処理はクエリメソッドをイントロスペクトし、リポジトリクエリに関するメタデータを収集します。Spring Data は、このメタデータを同じパッケージ内のソースリポジトリにちなんで命名された JSON ファイルに保存します。リポジトリ JSON メタデータには、クエリとフラグメントに関する詳細が含まれます。以下のリポジトリの例を以下に示します。

  • メタデータ

  • リポジトリ

{
  "name": "example.springdata.UserRepository",
  "module": "JDBC",
  "type": "IMPERATIVE",
  "methods": [
    {
      "name": "findBy",
      "signature": "public abstract java.util.List<example.springdata.User> example.springdata.UserRepository.findBy()",
      "query": {
        "query": "SELECT * FROM User"
      }
    },
    {
      "name": "findByLastnameStartingWith",
      "signature": "public abstract org.springframework.data.domain.Page<example.springdata.User> example.springdata.UserRepository.findByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)",
      "query": {
        "query": "SELECT * FROM User u WHERE lastname LIKE :lastname",
        "count-query": "SELECT COUNT(*) FROM User WHERE lastname LIKE :lastname"
      }
    },
    {
      "name": "findByEmailAddress",
      "signature": "public abstract example.springdata.User example.springdata.UserRepository.findByEmailAddress(java.lang.String)",
      "query": {
        "query": "select * from User where emailAddress = ?1"
      }
    },
interface UserRepository extends CrudRepository<User, Integer> {

  List<User> findBy();

  Page<User> findByLastnameStartingWith(String lastname, Pageable page);

  @Query("select * from User where emailAddress = ?1")
  User findByEmailAddress(String username);
}

JSON メタデータの作成は、spring.aot.repositories.metadata.enabled フラグを介して制御できます。

ネイティブイメージランタイムのヒント

アプリケーションをネイティブイメージとして実行するには、通常の JVM ランタイムに比べて追加の情報が必要です。Spring Data は、ネイティブイメージの使用に関する AOT 処理中に実行時のヒントを提供します。これらは特に、以下の点に関するヒントです。

  • 監査

  • クラスパススキャンの結果をキャプチャーするための ManagedTypes 

  • リポジトリ

    • エンティティ、戻り値の型、Spring Data アノテーションのリフレクションヒント

    • リポジトリフラグメント

    • Querydsl Q クラス

    • Kotlin コルーチンサポート

  • Web サポート (Jackson の PagedModel のヒント)