Spring

@ServiceConnectionとは|Spring BootのTestcontainers接続設定を自動化する方法

@ServiceConnectionとは|Spring BootのTestcontainers接続設定を自動化する方法

Testcontainersでテスト用のデータベースを立ち上げると、ポート番号がテストのたびに変わります。この動的なポートをSpring Bootへ伝えるために、かつては@DynamicPropertySourceでspring.datasource.urlを手書きするのが定番でした。Spring Boot 3.1で追加された@ServiceConnectionは、この受け渡しをまるごと肩代わりします。本記事が扱うのはSpring Bootのテスト支援機能であり、Androidのandroid.content.ServiceConnectionやAzure Pipelinesのサービス接続とは別の概念です。最小構成のコード、@DynamicPropertySourceとの使い分け、Spring Boot 3.5系と4系で依存関係の書き方が変わる点を、公開アーティファクトを実際に確認した結果に基づいて整理します。

まとめ

@ServiceConnectionはコンテナの接続情報をSpring BootのConnectionDetailsへ自動登録するアノテーションで、Spring Boot 3.1.0(2023年5月18日GA)以降で使えます。利用にはspring-boot-testcontainersをテスト依存に追加し、JUnit 5の@Testcontainersと@Containerを必ず併記します。この3点が揃わないとコンテナが起動しません。

注意が要るのはバージョンの組み合わせです。Spring Boot 3.5.16はTestcontainers 1.21.4を、4.1.0は2.0.5を管理します。Testcontainers 2.0はアーティファクトIDとパッケージを両方変更しているため、org.testcontainers:mysqlのような従来の記述はSpring Boot 4へ上げた瞬間に解決できなくなります。以下、それぞれの根拠と具体的な書き換え方を見ていきます。

@ServiceConnectionが肩代わりする接続設定

@ServiceConnectionはorg.springframework.boot.testcontainers.service.connectionパッケージに属します。コンテナのインスタンスに付けると、Spring Bootがそのコンテナの種類を判別し、ホスト・ポート・ユーザー名・パスワードといった接続情報をConnectionDetailsのBeanとして登録します。テスト側でspring.datasource.urlを一切書かなくてよくなるのは、このBeanがapplication.yml由来の設定よりも優先されるためです。

最小構成のテストクラス

Spring Boot 4.1とTestcontainers 2.0.5の組み合わせでは、次の形が最小構成です。

import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.mysql.MySQLContainer;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;

@Testcontainers
@SpringBootTest
class OrderRepositoryTests {

    @Container
    @ServiceConnection
    static MySQLContainer mysql = new MySQLContainer("mysql:8.4");

    @Test
    void 保存した注文を検索できる() {
        // spring.datasource.url を書かなくても mysql コンテナへ接続される
    }
}

3つのアノテーションはそれぞれ役割が違います。@TestcontainersはJUnit 5の拡張を有効にし、@Containerが付いたフィールドのコンテナを起動・停止させます。@ServiceConnectionは起動したコンテナから接続情報を取り出してSpringに渡す係です。@ServiceConnectionだけを書いてもコンテナは起動しません。Spring Bootの公式リファレンスの例も@Containerと@ServiceConnectionを並べて記述しています。フィールドはstaticである必要があり、インスタンスフィールドに付けると起動時に検証エラーになります。

イメージタグをmysql:latestにしない理由は再現性です。latestのままだと、コードを一行も変えていないのに別のメジャーバージョンが降ってきてテストが壊れます。長期サポート系列のmysql:8.4のように固定してください。

対応ミドルウェアと未対応時に発生する例外

Spring Bootが標準で接続情報を解決できるのは、JDBC・R2DBCの主要データベース(PostgreSQL、MySQL、MariaDB、Oracle、SQL Server、ClickHouse)に加え、MongoDB、Neo4j、Cassandra、Couchbase、Redis、Elasticsearch、Kafka、Redpanda、RabbitMQ、Pulsar、ActiveMQ、Artemis、LDAP、Zipkin、OpenTelemetryなどです。FlywayとLiquibaseも対象に含まれます。

ここに載っていないコンテナに@ServiceConnectionを付けた場合、黙って無視されるわけではありません。ConnectionDetailsRegistrarはファクトリの探索を必須扱いで呼び出すため、該当するファクトリが見つからないとアプリケーションコンテキストの起動時に例外が飛びます。

No ConnectionDetailsFactory found for source '...'.
You may need to add a 'name' to your @ServiceConnection annotation

後半の一文はname属性が未指定のときだけ追記されます。このメッセージが出たら、対応外のサービスなのか次項のname指定が足りないだけなのかを切り分けます。起動時点で落ちるぶん、原因の特定は容易です。

name属性によるイメージ判別の補助

Spring Bootは既定でDockerイメージ名からサービスの種類を判別します。MySQLContainerのような専用クラスを使っている限り、この判別は自動的に効きます。問題になるのはGenericContainerで任意のイメージを立ち上げる場合です。イメージ名から種類を推定できないため、name属性で明示します。

@Container
@ServiceConnection(name = "redis")
static GenericContainer<?> redis =
        new GenericContainer<>("mycompany/custom-redis:1.4").withExposedPorts(6379);

nameにはvalueというエイリアスがあるため、@ServiceConnection("redis")とも書けます。同じコンテナから複数種類のConnectionDetailsが作られてしまう場合は、type属性に生成を許可する型を指定して絞り込みます。Spring Bootのアノテーション全体の役割分担はSpring Bootの主要なアノテーション一覧|DI・MVC・JPA・バリデーション・AOPを役割別に整理で整理しています。

@DynamicPropertySourceとの使い分け

@DynamicPropertySourceはSpring Framework 5.2.5から使える仕組みで、DynamicPropertyRegistryに対してプロパティ名とサプライヤ関数の組を登録します。@ServiceConnectionが登場した後も廃止されていません。両者は置き換えの関係ではなく、守備範囲が違います。

観点 @ServiceConnection @DynamicPropertySource
対象 Spring Boot対応済みのサービス 任意のプロパティ
記述量 アノテーション1行 staticメソッドと登録処理
プロパティ名の指定 不要 必須
未対応サービス 起動時に例外 使用可
最低バージョン Spring Boot 3.1 Spring Framework 5.2.5

同じデータベース接続を両方式で書くと差は一目瞭然です。従来型は次のようにプロパティ名を3つ手書きします。

// 従来: プロパティ名を自分で組み立てる
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.4");

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
    registry.add("spring.datasource.url", mysql::getJdbcUrl);
    registry.add("spring.datasource.username", mysql::getUsername);
    registry.add("spring.datasource.password", mysql::getPassword);
}
// 現行: アノテーション1行で同じ結果になる
@Container
@ServiceConnection
static MySQLContainer mysql = new MySQLContainer("mysql:8.4");

行数以上に効くのが、綴り間違いの余地が消える点です。spring.datasource.urlをspring.datasource.uriと書いてもコンパイルは通り、テストは接続エラーという分かりにくい形で落ちます。

一方、対応表に無いサービスやアプリ固有の設定値には引き続き@DynamicPropertySourceを使います。データベース接続を@ServiceConnectionに任せつつ、独自プロパティだけを登録する併用も可能です。

@Container
static GenericContainer<?> opensearch =
        new GenericContainer<>("opensearchproject/opensearch:2.19.0").withExposedPorts(9200);

@DynamicPropertySource
static void registerSearchEndpoint(DynamicPropertyRegistry registry) {
    registry.add("app.search.endpoint",
            () -> "http://" + opensearch.getHost() + ":" + opensearch.getFirstMappedPort());
}

Spring Bootのバージョン別に変わる依存関係

@ServiceConnectionを使うにはspring-boot-testcontainersが必要です。ただしSpring Boot 4でこのモジュールの中身が大きく変わりました。実際に公開されているjarを開くと、3.5.16では121クラスありservice/connection配下にjdbc・kafka・mongo・neo4j・redisなど19サービス分のファクトリが同梱されています。対して4.1.0は45クラスで、サービス別のファクトリが一切入っていません。

Spring Boot 3.5.16での依存指定(接頭辞なし)

Spring Boot 3.5.16が管理するTestcontainersは1.21.4です。この系列ではアーティファクトIDに接頭辞が付きません。

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-testcontainers'
    testImplementation 'org.testcontainers:junit-jupiter'
    testImplementation 'org.testcontainers:mysql'
}

バージョン番号を書いていないのは、Spring BootのBOMがTestcontainersのバージョンを管理しているためです。個別に1.18.3のような固定値を書くと、Spring Bootを上げたときに管理バージョンとずれて不整合の原因になります。

Spring Boot 4.1.0で必要になる技術別モジュール

Spring Boot 4.1.0が管理するTestcontainersは2.0.5です。アーティファクトIDが変わっているため、3.5系の記述をそのまま持ち込むと依存解決に失敗します。

dependencies {
    testImplementation 'org.springframework.boot:spring-boot-testcontainers'
    testImplementation 'org.testcontainers:testcontainers-junit-jupiter'
    testImplementation 'org.testcontainers:testcontainers-mysql'
}

加えて、サービス別のファクトリが移動した先を押さえておく必要があります。JDBC系はspring-boot-jdbc、MongoDBはspring-boot-mongodb、Kafkaはspring-boot-kafkaにそれぞれ含まれます。ただし通常のスターターを使っていれば意識する必要はありません。spring-boot-starter-data-jpaのpomを確認するとspring-boot-jdbcを推移的に含んでいるため、追加指定なしでファクトリがクラスパスに載ります。

気を付けたいのはスターターを使わず個別モジュールを組み合わせている構成です。spring-boot-testcontainersを入れただけでは対応するファクトリが存在せず、前述のNo ConnectionDetailsFactory found for sourceで起動に失敗します。Spring Boot 4での構成変更全般はSpring Boot 4とは?最新バージョン4.1の変更点・新機能とSpring Boot 3との違いを解説【2026年最新】に、どのバージョンを選ぶべきかはSpring Bootバージョン一覧とサポート期限【2026年7月】最新4.1と3.5 EOL後の選び方にまとめています。

Testcontainers 2.0で書き換わるクラス名

Testcontainers 2.0.0は2025年10月14日にリリースされ、3つの破壊的変更が入りました。JUnit 4サポートの削除、全モジュールへのtestcontainers-接頭辞の付与、そしてコンテナクラスのorg.testcontainers.<モジュール名>パッケージへの移動です。Spring Boot 4へ上げると自動的に2.0系が使われるため、この移行は避けて通れません。

アーティファクトIDの変更は完全な破壊的変更です。org.testcontainers:mysqlは1.21.4が最終版で、2.0系は公開されていません。実際にorg/testcontainers/mysql/2.0.5/mysql-2.0.5.pomをMaven Centralから取得しようとするとHTTP 404が返ります。Spring Boot 4へ上げてビルドが「該当バージョンが見つからない」で落ちる場合、原因はほぼこれです。

一方でクラスの移動は互換性が残されています。testcontainers-mysql:2.0.5のjarを開くと、新しいorg.testcontainers.mysql.MySQLContainerと並んで旧来のorg.testcontainers.containers.MySQLContainerも同梱されています。ソースを読むと後者には@Deprecatedが付き、javadocに「use org.testcontainers.mysql.MySQLContainer instead」と明記されています。import文を直さなくても当面は動くものの、非推奨警告が出続けます。

移行時に見落としやすいのが、新クラスでジェネリクスが外れている点です。旧クラスは自己参照型パラメータを持つMySQLContainer<SELF extends MySQLContainer<SELF>>でしたが、新クラスはpublic class MySQLContainer extends JdbcDatabaseContainer<MySQLContainer>という宣言です。そのためフィールド定義の書き方まで変わります。

項目 Testcontainers 1.21.4 Testcontainers 2.0.5
アーティファクト org.testcontainers:mysql org.testcontainers:testcontainers-mysql
JUnit連携 org.testcontainers:junit-jupiter org.testcontainers:testcontainers-junit-jupiter
コンテナクラス org.testcontainers.containers org.testcontainers.mysql
フィールド宣言 MySQLContainer<?> MySQLContainer
JUnit 4 利用可 削除済
管理元 Spring Boot 3.5.16 Spring Boot 4.1.0

アノテーション側は変わりません。@Testcontainersと@Containerは2.0.5のjarでもorg.testcontainers.junit.jupiterパッケージのままです。書き換えが必要なのはコンテナクラスのimportだけと考えてよいでしょう。

Kafkaを使っている場合はクラス名にも注意が要ります。2.x系ではorg.testcontainers.kafka.KafkaContainerがApache Kafka公式イメージ向け、org.testcontainers.kafka.ConfluentKafkaContainerがConfluent Platformイメージ向けと分かれました。従来confluentinc/cp-kafkaを指定していたコードは後者へ移してください。JUnit 5そのものの基礎はJUnitとは?Javaの単体テストフレームワークの基本と書き方【2026年版】で解説しています。

未対応サービス向けのファクトリ自作

対応リストに無いミドルウェアでも、ContainerConnectionDetailsFactoryを実装すれば@ServiceConnectionの対象にできます。社内独自のサービスをコンテナ化してテストしているケースでは、この自作が現実的な選択肢になります。

手順は3段階です。まず接続情報を表すConnectionDetailsのインタフェースを定義します。

public interface CustomSearchConnectionDetails extends ConnectionDetails {
    String getEndpoint();
}

次にContainerConnectionDetailsFactoryを継承したファクトリを作ります。型引数はコンテナ型とConnectionDetails型の2つで、コンストラクタで受け付けるコンテナ名を指定します。

class CustomSearchContainerConnectionDetailsFactory
        extends ContainerConnectionDetailsFactory<Container<?>, CustomSearchConnectionDetails> {

    CustomSearchContainerConnectionDetailsFactory() {
        super("custom-search");
    }

    @Override
    protected CustomSearchConnectionDetails getContainerConnectionDetails(
            ContainerConnectionSource<Container<?>> source) {
        return new CustomSearchConnectionDetails() {
            @Override
            public String getEndpoint() {
                return "http://" + source.getContainer().getHost();
            }
        };
    }
}

最後にMETA-INF/spring.factoriesへ登録します。キーはConnectionDetailsFactoryの完全修飾名です。

org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\
  com.example.testsupport.CustomSearchContainerConnectionDetailsFactory

この登録方式はSpring Boot自身が使っているものと同じで、spring-boot-jdbcのspring.factoriesにも同じキーの下にJdbcContainerConnectionDetailsFactoryが並んでいます。自作前にそのファイルを手本として読むのが近道です。なおコンストラクタに渡したcustom-searchがname属性と突き合わされる名前になるため、使う側は@ServiceConnection(name = "custom-search")と書きます。

ローカル開発でのコンテナ自動起動

Testcontainersはテスト実行時だけでなく、ローカルでアプリを起動する際の依存サービス起動にも使えます。開発用のデータベースを手動で立ち上げる手間がなくなるため、実務での効果はテスト用途より大きいこともあります。

手順は2つです。まずコンテナをBeanとして定義する@TestConfigurationクラスを作ります。フィールドではなく@Beanメソッドに@ServiceConnectionを付ける点が、テストクラスでの書き方との違いです。

@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfiguration {

    @Bean
    @ServiceConnection
    public MySQLContainer mysqlContainer() {
        return new MySQLContainer("mysql:8.4");
    }
}

次に、テストソースセット側に起動用のmainクラスを置きます。SpringApplication.fromで本番のmainメソッドを参照し、withでコンテナ定義を差し込む形です。

public class TestMyApplication {

    public static void main(String[] args) {
        SpringApplication.from(MyApplication::main)
            .with(ContainersConfiguration.class)
            .run(args);
    }
}

このクラスを実行すると、コンテナが起動してからアプリが立ち上がり、接続設定も自動で解決されます。コンテナ定義を通常のクラスとして書きたい場合は@ImportTestcontainersで外部クラスから取り込む方法もあります。

似た目的の機能にDocker Composeサポートがあります。判断軸は依存サービスの定義をどこに置きたいかです。compose.yamlが既にあり、Java以外のメンバーやCIも同じ定義を使うならDocker Composeサポートが向きます。定義をJavaコード側に閉じて型安全に扱いたいならTestcontainersです。spring-boot-jdbcには両方のファクトリが並んで登録されており、どちらの経路でも同じConnectionDetailsに到達します。

コンテナ再利用によるテスト起動時間の短縮

Testcontainersは既定でテスト終了時にコンテナを破棄します。再現性の面では正しい挙動ですが、開発中に同じテストを何度も回すと毎回の起動時間が積み上がります。これを避ける仕組みが再利用機能です。

ここは誤った情報が出回りやすい箇所です。再利用を有効にするアノテーションは存在しません。必要なのは次の3点セットです。

// 1. 再利用を宣言し、コンテナを手動で起動する
//    @Container は付けない(JUnit連携が stop() を呼ぶと再利用が成立しないため)
static MySQLContainer mysql = new MySQLContainer("mysql:8.4").withReuse(true);

static {
    mysql.start();
}
# 2. ~/.testcontainers.properties に記述する
testcontainers.reuse.enable=true

3点目は、stop()を直接またはtry-with-resources経由で呼ばないことです。公式ドキュメントは「start()を手動で呼び、try-with-resourcesやJUnit連携を介してstop()を呼ばないこと」を条件として明示しています。本記事の他の例のように@Containerを付けると、JUnitの拡張がテスト終了時にコンテナを停止するため再利用は働きません。

プロパティは環境変数TESTCONTAINERS_REUSE_ENABLE=trueでも指定できます。ただしクラスパス上のpropertiesファイルに書いても効きません。有効化を利用者ごとの判断に委ねる意図があるため、リポジトリにコミットする場所ではなくホームディレクトリの設定ファイルか環境変数に限定されています。JDBC URL方式なら末尾に?TC_REUSABLE=trueを付けます。

制約も押さえてください。公式ドキュメントは再利用機能を「実験的機能であり挙動が変わりうる」「CI利用には向かない」と位置づけています。コンテナが残るぶんデータベースの状態も次のテストへ持ち越されるため、ローカル開発時の各自の設定に留めるのが安全です。

よくある質問

@ServiceConnectionはどのSpring Bootバージョンから使えますか

Spring Boot 3.1.0(2023年5月18日GA、日付はUTC基準)からです。それ以前では@DynamicPropertySourceで接続情報を登録する必要があります。現在のGA最新版は4.1.0(2026年6月10日GA)で、Java 17から26に対応し、Maven 3.6.3以降またはGradle 8.14以降・9系が必要です。3.1系はOSSサポートが終了しているため、新規採用なら4.1系か3.5系を選びます。

@ServiceConnectionを付けたのに接続設定が反映されません

まず@Testcontainersと@Containerが併記されているか、フィールドがstaticかを確認してください。コンテナが起動していないならこのいずれかが原因です。No ConnectionDetailsFactory found for sourceが出る場合は、対応リストに無いミドルウェアか、GenericContainerでname属性が足りていません。Spring Boot 4系でスターターを使わない構成なら、技術別モジュール不足も疑ってください。

Spring Boot 4へ上げたらTestcontainersの依存解決が失敗します

アーティファクトIDの変更が原因です。Spring Boot 4.1.0はTestcontainers 2.0.5を管理しますが、org.testcontainers:mysqlは1.21.4が最終版で2.0系が存在しません。org.testcontainers:testcontainers-mysqlのようにtestcontainers-接頭辞付きへ書き換えてください。junit-jupiterも同様にtestcontainers-junit-jupiterへ変わります。JUnit 4のテストが残っていれば、2.0でサポートが削除されたためJUnit 5への移行も必要です。

@DynamicPropertySourceはもう使わなくてよいのですか

Spring Boot対応済みのミドルウェアは@ServiceConnectionで置き換えられますが、対応リストに無いサービスやアプリ固有のプロパティには引き続き必要で、廃止予定もありません。実務ではデータベース接続を@ServiceConnectionに任せ、独自プロパティだけ@DynamicPropertySourceで登録する併用が最も多くなります。頻繁に使うミドルウェアなら、ファクトリを自作して@ServiceConnection側へ寄せる手もあります。

テスト用プロパティを上書きしたい場合はどうしますか

@SpringBootTestのproperties属性か@TestPropertySourceを使います。ただし@ServiceConnectionが登録するConnectionDetailsのBeanは接続系プロパティより優先されるため、spring.datasource.urlを上書きしてもコンテナ側の値が使われます。公式ドキュメントも接続詳細が設定プロパティに優先すると明記しています。接続先を別サーバへ向けるなら@ServiceConnectionを外してください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2025.02.27 コラム トラッキングビーコン(Webビーコン)とは?仕組み・用途・確認と対策を徹底解説
  2. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  3. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  4. 2026.07.09 テックブログ Muse Imageとは?Metaの画像生成AIの使い方・API料金(1枚0.01ドル)と日本での提供状況【2026年10月】
  5. 2026.10.21 テックブログ ユミルリンク不正アクセス第二報で延べ5,162万件:Cuenote FC利用企業と受信者の確認点

RELATED POSTS 関連記事

目次