此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Boot 4.0.4!spring-doc.cadn.net.cn

开发时服务

开发时服务提供在开发应用程序期间运行所需的各种外部依赖。 这些服务仅应在开发过程中使用,当应用程序部署后即被禁用。spring-doc.cadn.net.cn

Spring Boot 提供对两种开发时服务的支持:Docker Compose 和 Testcontainers。 下一节将详细介绍这两项服务。spring-doc.cadn.net.cn

Docker Compose 支持

Docker Compose 是一项广受欢迎的技术,可用于定义和管理应用程序所需服务的多个容器。 通常会在应用程序旁边创建一个 docker-compose.yml 文件,用于定义和配置服务容器。spring-doc.cadn.net.cn

使用 Docker Compose 的典型工作流程是:先运行 docker compose up,然后在应用程序中连接已启动的服务进行开发,完成后运行 docker compose downspring-doc.cadn.net.cn

spring-boot-docker-compose 模块可被引入项目中,以提供对使用 Docker Compose 管理容器的支持。 请按以下 Maven 和 Gradle 构建配置示例所示,在构建文件中添加该模块依赖项:spring-doc.cadn.net.cn

Maven
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-docker-compose</artifactId>
		<optional>true</optional>
	</dependency>
</dependencies>
Gradle
dependencies {
	developmentOnly("org.springframework.boot:spring-boot-docker-compose")
}

当此模块作为依赖项引入时,Spring Boot 将执行以下操作:spring-doc.cadn.net.cn

如果在启动应用程序时 Docker Compose 服务已处于运行状态,Spring Boot 将仅为每个受支持的容器创建服务连接 Bean。 它不会再次调用 docker compose up,且在应用程序关闭时也不会调用 docker compose stopspring-doc.cadn.net.cn

重新打包的归档文件默认不包含 Spring Boot 的 Docker Compose 支持。 如需使用此功能,您需要显式引入该支持。 若使用 Maven 插件,请将 excludeDockerCompose 属性设置为 false。 若使用 Gradle 插件,请配置任务的类路径以包含 developmentOnly 配置

前提条件

您需要将 dockerdocker compose(或 docker-compose)CLI 应用程序添加到您的系统路径中。 支持的最低 Docker Compose 版本为 2.2.0。spring-doc.cadn.net.cn

服务连接

服务连接是指与任何远程服务的连接。 Spring Boot 的自动配置可以读取服务连接的详细信息,并利用这些信息建立与远程服务的连接。 在此过程中,连接详细信息的优先级高于任何与连接相关的配置属性。spring-doc.cadn.net.cn

使用 Spring Boot 的 Docker Compose 支持时,服务连接将建立到容器所映射的端口。spring-doc.cadn.net.cn

Docker Compose 通常以这种方式使用:将容器内部的端口映射到您计算机上的临时端口。 例如,PostgreSQL 服务器可能在容器内使用端口 5432 运行,但在本地被映射到一个完全不同的端口。 服务连接将始终自动发现并使用该本地映射的端口。

服务连接通过使用容器的镜像名称来建立。 当前支持以下服务连接:spring-doc.cadn.net.cn

连接详情 匹配于

ActiveMQConnectionDetailsspring-doc.cadn.net.cn

名为“symptoma/activemq”或“apache/activemq-classic”的容器spring-doc.cadn.net.cn

ArtemisConnectionDetailsspring-doc.cadn.net.cn

名为“apache/activemq-artemis”的容器spring-doc.cadn.net.cn

CassandraConnectionDetailsspring-doc.cadn.net.cn

名为"cassandra"的容器spring-doc.cadn.net.cn

ElasticsearchConnectionDetailsspring-doc.cadn.net.cn

名为"elasticsearch"的容器spring-doc.cadn.net.cn

HazelcastConnectionDetailsspring-doc.cadn.net.cn

名为“hazelcast/hazelcast”的容器。spring-doc.cadn.net.cn

JdbcConnectionDetailsspring-doc.cadn.net.cn

名为"clickhouse/clickhouse-server"、"gvenzl/oracle-free"、"gvenzl/oracle-xe"、"mariadb"、"mssql/server"、"mysql"或"postgres"的容器spring-doc.cadn.net.cn

LdapConnectionDetailsspring-doc.cadn.net.cn

名为“osixia/openldap”和“lldap/lldap”的容器spring-doc.cadn.net.cn

MongoConnectionDetailsspring-doc.cadn.net.cn

名为"mongo"的容器spring-doc.cadn.net.cn

Neo4jConnectionDetailsspring-doc.cadn.net.cn

名为"neo4j"的容器spring-doc.cadn.net.cn

OtlpLoggingConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”和“grafana/otel-lgtm”的容器spring-doc.cadn.net.cn

OtlpMetricsConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”和“grafana/otel-lgtm”的容器spring-doc.cadn.net.cn

OtlpTracingConnectionDetailsspring-doc.cadn.net.cn

名为“otel/opentelemetry-collector-contrib”和“grafana/otel-lgtm”的容器spring-doc.cadn.net.cn

PulsarConnectionDetailsspring-doc.cadn.net.cn

名为“apachepulsar/pulsar”的容器spring-doc.cadn.net.cn

R2dbcConnectionDetailsspring-doc.cadn.net.cn

名为"clickhouse/clickhouse-server"、"gvenzl/oracle-free"、"gvenzl/oracle-xe"、"mariadb"、"mssql/server"、"mysql"或"postgres"的容器spring-doc.cadn.net.cn

RabbitConnectionDetailsspring-doc.cadn.net.cn

名为"rabbitmq"的容器spring-doc.cadn.net.cn

DataRedisConnectionDetailsspring-doc.cadn.net.cn

名为"redis"、"redis/redis-stack"或"redis/redis-stack-server"的容器spring-doc.cadn.net.cn

ZipkinConnectionDetailsspring-doc.cadn.net.cn

名为“openzipkin/zipkin”的容器。spring-doc.cadn.net.cn

SSL 支持

某些镜像开箱即用支持 SSL,或者您可能希望为容器启用 SSL,以模拟您的生产环境配置。 Spring Boot 支持对已支持的服务连接进行 SSL 配置。 请注意,您仍需自行在容器内运行的服务上启用 SSL;此功能仅在您的应用程序中配置客户端侧的 SSL。spring-doc.cadn.net.cn

以下服务连接支持 SSL:spring-doc.cadn.net.cn

要为服务启用SSL支持,您可以使用服务标签spring-doc.cadn.net.cn

对于基于 JKS 的密钥库和信任库,您可以使用以下容器标签:spring-doc.cadn.net.cn

这些标签与SSL 绑定包可用的属性相对应。spring-doc.cadn.net.cn

对于基于 PEM 的密钥库和信任库,您可以使用以下容器标签:spring-doc.cadn.net.cn

这些标签与SSL 绑定包可用的属性相对应。spring-doc.cadn.net.cn

以下示例为 Redis 容器启用 SSL:spring-doc.cadn.net.cn

services:
  redis:
    image: 'redis:latest'
    ports:
      - '6379'
    secrets:
      - ssl-ca
      - ssl-key
      - ssl-cert
    command: 'redis-server --tls-port 6379 --port 0 --tls-cert-file /run/secrets/ssl-cert --tls-key-file /run/secrets/ssl-key --tls-ca-cert-file /run/secrets/ssl-ca'
    labels:
      - 'org.springframework.boot.sslbundle.pem.keystore.certificate=client.crt'
      - 'org.springframework.boot.sslbundle.pem.keystore.private-key=client.key'
      - 'org.springframework.boot.sslbundle.pem.truststore.certificate=ca.crt'
secrets:
  ssl-ca:
    file: 'ca.crt'
  ssl-key:
    file: 'server.key'
  ssl-cert:
    file: 'server.crt'

自定义镜像

有时,您可能需要使用自己的镜像来提供服务。 只要您的自定义镜像的行为方式与标准镜像一致,即可使用任意自定义镜像。 具体而言,标准镜像所支持的任何环境变量,也必须在您的自定义镜像中使用。spring-doc.cadn.net.cn

如果您的镜像使用了不同的名称,您可以在 compose.yml 文件中使用标签,以便 Spring Boot 能够提供服务连接。 请使用名为 org.springframework.boot.service-connection 的标签来指定服务名称。spring-doc.cadn.net.cn

services:
  redis:
    image: 'mycompany/mycustomredis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.service-connection: redis

跳过特定容器

如果在您的 compose.yml 中定义了一个容器镜像,且您不希望该镜像与您的应用程序关联,则可以使用标签将其忽略。 任何带有 org.springframework.boot.ignore 标签的容器都将被 Spring Boot 忽略。spring-doc.cadn.net.cn

services:
  redis:
    image: 'redis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.ignore: true

使用特定的 Compose 文件

如果您的 Compose 文件未与应用程序位于同一目录下,或者文件名不同,则可以在您的 spring.docker.compose.fileapplication.properties 中使用 application.yaml 指向其他文件。 属性可定义为绝对路径,也可定义为相对于应用程序的相对路径。spring-doc.cadn.net.cn

spring.docker.compose.file=../my-compose.yml
spring:
  docker:
    compose:
      file: "../my-compose.yml"

等待容器就绪

由 Docker Compose 启动的容器可能需要一些时间才能完全就绪。 推荐的就绪状态检查方法是在您的 compose.yml 文件的服务定义下添加一个 healthcheck 部分。spring-doc.cadn.net.cn

由于在 compose.yml 文件中省略 healthcheck 配置的情况并不少见,Spring Boot 还会直接检查服务是否就绪。 默认情况下,当能够通过 TCP/IP 连接到容器映射的端口时,即认为该容器已就绪。spring-doc.cadn.net.cn

您可以通过在 compose.yml 文件中添加一个 org.springframework.boot.readiness-check.tcp.disable 标签,按容器单独禁用此功能。spring-doc.cadn.net.cn

services:
  redis:
    image: 'redis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.readiness-check.tcp.disable: true

您还可以在 application.propertiesapplication.yaml 文件中修改超时值:spring-doc.cadn.net.cn

spring.docker.compose.readiness.tcp.connect-timeout=10s
spring.docker.compose.readiness.tcp.read-timeout=5s
spring:
  docker:
    compose:
      readiness:
        tcp:
          connect-timeout: 10s
          read-timeout: 5s

总体超时时间可通过 spring.docker.compose.readiness.timeout 进行配置。spring-doc.cadn.net.cn

控制 Docker Compose 生命周期

默认情况下,Spring Boot 在应用程序启动时调用 docker compose up,在关闭时调用 docker compose stop。 如果您希望采用不同的生命周期管理方式,可以使用 spring.docker.compose.lifecycle-management 属性。spring-doc.cadn.net.cn

以下值受支持:spring-doc.cadn.net.cn

此外,您还可以使用 spring.docker.compose.start.command 属性来更改是使用 docker compose up 还是 docker compose startspring.docker.compose.stop.command 允许您配置是使用 docker compose down 还是 docker compose stopspring-doc.cadn.net.cn

您还可以向 Docker Compose 命令传递额外参数。 spring.docker.compose.arguments 属性允许您指定传递给所有 Docker Compose 命令的参数。 spring.docker.compose.start.arguments 属性允许您指定仅传递给 up(或 start)命令的参数,而 spring.docker.compose.stop.arguments 属性则允许您指定仅传递给 down(或 stop)命令的参数。spring-doc.cadn.net.cn

以下示例展示了如何配置生命周期管理:spring-doc.cadn.net.cn

spring.docker.compose.lifecycle-management=start-and-stop
spring.docker.compose.arguments[0]=--project-name=myapp
spring.docker.compose.arguments[1]=--progress=auto
spring.docker.compose.start.command=up
spring.docker.compose.start.arguments[0]=--build
spring.docker.compose.start.arguments[1]=--force-recreate
spring.docker.compose.stop.command=down
spring.docker.compose.stop.timeout=1m
spring.docker.compose.stop.arguments[0]=--volumes
spring.docker.compose.stop.arguments[1]=--remove-orphans
spring:
  docker:
    compose:
      lifecycle-management: start-and-stop
      arguments:
        - "--project-name=myapp"
        - "--progress=auto"
      start:
        command: up
        arguments:
          - "--build"
          - "--force-recreate"
      stop:
        command: down
        timeout: 1m
        arguments:
          - "--volumes"
          - "--remove-orphans"

激活 Docker Compose 配置文件

Docker Compose 配置文件(profiles)与 Spring 配置文件类似,均可让您针对特定环境调整 Docker Compose 配置。 如需启用特定的 Docker Compose 配置文件,您可在 spring.docker.compose.profiles.activeapplication.properties 文件中使用 application.yaml 属性:spring-doc.cadn.net.cn

spring.docker.compose.profiles.active=myprofile
spring:
  docker:
    compose:
      profiles:
        active: "myprofile"

在测试中使用 Docker Compose

默认情况下,Spring Boot 的 Docker Compose 支持在运行测试时被禁用。spring-doc.cadn.net.cn

如需在测试中启用 Docker Compose 支持,请将 spring.docker.compose.skip.in-tests 设置为 falsespring-doc.cadn.net.cn

使用 Gradle 时,您还需将 spring-boot-docker-compose 依赖项的配置从 developmentOnly 更改为 testAndDevelopmentOnlyspring-doc.cadn.net.cn

Gradle
dependencies {
	testAndDevelopmentOnly("org.springframework.boot:spring-boot-docker-compose")
}

Testcontainers 支持

除了使用 Testcontainers 进行集成测试之外,还可以在开发阶段使用它们。 接下来的章节将对此进行更详细的介绍。spring-doc.cadn.net.cn

在开发阶段使用 Testcontainers

这种方案使开发者能够快速启动应用程序所依赖服务的容器,从而无需手动配置数据库服务器等组件。 以这种方式使用 Testcontainers 提供的功能与 Docker Compose 类似,区别在于容器配置采用 Java 编写,而非 YAML 格式。spring-doc.cadn.net.cn

在开发阶段使用 Testcontainers 时,您需要使用“test”类路径(而非“main”类路径)来启动应用程序。 这将使您能够访问所有已声明的测试依赖项,并为您提供一个编写测试配置的自然位置。spring-doc.cadn.net.cn

要创建应用程序的可测试启动版本,您应在 src/test 目录中创建一个“Application”类。 例如,如果您的主应用程序位于 src/main/java/com/example/MyApplication.java 中,则应创建 src/test/java/com/example/TestMyApplication.javaspring-doc.cadn.net.cn

TestMyApplication 类可使用 SpringApplication.from(…​) 方法启动实际应用程序:spring-doc.cadn.net.cn

import org.springframework.boot.SpringApplication;

public class TestMyApplication {

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

}
import org.springframework.boot.fromApplication

fun main(args: Array<String>) {
	fromApplication<MyApplication>().run(*args)
}

您还需要定义希望随应用程序一同启动的 Container 实例。 为此,您需要确保已将 spring-boot-testcontainers 模块作为 test 依赖项添加。 完成此操作后,您可以创建一个 @TestConfiguration 类,并在其中为希望启动的容器声明 @Bean 方法。spring-doc.cadn.net.cn

您还可以使用 @ServiceConnection 注解您的 @Bean 方法,以创建 ConnectionDetails 类型的 Bean。 有关受支持技术的详细信息,请参阅 服务连接 章节。spring-doc.cadn.net.cn

典型的 Testcontainers 配置如下所示:spring-doc.cadn.net.cn

import org.testcontainers.neo4j.Neo4jContainer;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

	@Bean
	@ServiceConnection
	public Neo4jContainer neo4jContainer() {
		return new Neo4jContainer("neo4j:5");
	}

}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.neo4j.Neo4jContainer

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

	@Bean
	@ServiceConnection
	fun neo4jContainer(): Neo4jContainer {
		return Neo4jContainer("neo4j:5")
	}

}
Container Bean 的生命周期由 Spring Boot 自动管理。 容器将自动启动和停止。
您可以使用 spring.testcontainers.beans.startup 属性来更改容器的启动方式。 默认使用 sequential 启动方式,但如果您希望并行启动多个容器,则可以选择 parallel

定义好测试配置后,即可使用 with(…​) 方法将其附加到测试Starters上:spring-doc.cadn.net.cn

import org.springframework.boot.SpringApplication;

public class TestMyApplication {

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

}
import org.springframework.boot.fromApplication
import org.springframework.boot.with

fun main(args: Array<String>) {
	fromApplication<MyApplication>().with(MyContainersConfiguration::class).run(*args)
}

现在,您可以像启动任何常规 Java main 方法应用程序一样启动 TestMyApplication,以启动您的应用程序及其运行所需的容器。spring-doc.cadn.net.cn

您可以使用 Maven 目标 spring-boot:test-run 或 Gradle 任务 bootTestRun 从命令行执行此操作。

在开发时提供动态属性

如果希望在开发阶段通过您的 Container @Bean 方法提供动态属性,请额外定义一个 DynamicPropertyRegistrar Bean。 注册器应通过一个 @Bean 方法进行定义,该方法将作为参数注入属性来源的容器。 此配置可确保在使用属性之前,容器已启动完毕。spring-doc.cadn.net.cn

典型的配置如下所示:spring-doc.cadn.net.cn

import org.testcontainers.mongodb.MongoDBContainer;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.DynamicPropertyRegistrar;

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

	@Bean
	public MongoDBContainer mongoDbContainer() {
		return new MongoDBContainer("mongo:5.0");
	}

	@Bean
	public DynamicPropertyRegistrar mongoDbProperties(MongoDBContainer container) {
		return (properties) -> {
			properties.add("spring.mongodb.host", container::getHost);
			properties.add("spring.mongodb.port", container::getFirstMappedPort);
		};
	}

}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.test.context.DynamicPropertyRegistrar;
import org.testcontainers.mongodb.MongoDBContainer

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

	@Bean
	fun mongoDbContainer(): MongoDBContainer {
		return MongoDBContainer("mongo:5.0")
	}
	
	@Bean
	fun mongoDbProperties(container: MongoDBContainer): DynamicPropertyRegistrar {
		return DynamicPropertyRegistrar { properties ->
			properties.add("spring.mongodb.host") { container.host }
			properties.add("spring.mongodb.port") { container.firstMappedPort }
		}
	}

}
在可能的情况下,建议使用 @ServiceConnection;但是,对于尚未支持 @ServiceConnection 的技术,动态属性可作为一种有用的备用方案。

导入 Testcontainers 声明类

使用 Testcontainers 时的一种常见模式是将 Container 实例声明为静态字段。 这些字段通常直接定义在测试类上。 它们也可以声明在父类中,或声明在测试类所实现的接口中。spring-doc.cadn.net.cn

例如,以下 MyContainers 接口声明了 mongoneo4j 容器:spring-doc.cadn.net.cn

import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.mongodb.MongoDBContainer;
import org.testcontainers.neo4j.Neo4jContainer;

import org.springframework.boot.testcontainers.service.connection.ServiceConnection;

public interface MyContainers {

	@Container
	@ServiceConnection
	MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");

	@Container
	@ServiceConnection
	Neo4jContainer neo4jContainer = new Neo4jContainer("neo4j:5");

}
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.mongodb.MongoDBContainer
import org.testcontainers.neo4j.Neo4jContainer

interface MyContainers {

	companion object {

		@Container
		@ServiceConnection
		@JvmField
		val mongoContainer = MongoDBContainer("mongo:5.0")

		@Container
		@ServiceConnection
		@JvmField
		val neo4jContainer = Neo4jContainer("neo4j:5")

	}

}

如果您已按此方式定义了容器,或您更倾向于这种风格,则可以导入这些声明类,而无需将容器定义为 @Bean 方法。 为此,请在测试配置类上添加 @ImportTestcontainers 注解:spring-doc.cadn.net.cn

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.context.ImportTestcontainers;

@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers.class)
public class MyContainersConfiguration {

}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.context.ImportTestcontainers

@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers::class)
class MyContainersConfiguration
如果您不打算使用服务连接功能,但希望改用@DynamicPropertySource,请从Container字段中移除@ServiceConnection注解。 您还可以在声明类中添加带@DynamicPropertySource注解的方法。

开发时使用 DevTools 与 Testcontainers

使用开发工具(devtools)时,您可以使用 @RestartScope 注解 Bean 及其方法。 此类 Bean 在开发工具重启应用时不会被重新创建。 这对于 Container 类型的 Bean 尤为有用,因为它们能在应用重启后保持自身状态。spring-doc.cadn.net.cn

import org.testcontainers.mongodb.MongoDBContainer;

import org.springframework.boot.devtools.restart.RestartScope;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

	@Bean
	@RestartScope
	@ServiceConnection
	public MongoDBContainer mongoDbContainer() {
		return new MongoDBContainer("mongo:5.0");
	}

}
import org.springframework.boot.devtools.restart.RestartScope
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.mongodb.MongoDBContainer

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

	@Bean
	@RestartScope
	@ServiceConnection
	fun mongoDbContainer(): MongoDBContainer {
		return MongoDBContainer("mongo:5.0")
	}

}
如果您使用 Gradle 并希望启用此功能,则需要将 spring-boot-devtools 依赖项的配置从 developmentOnly 更改为 testAndDevelopmentOnly。 在默认作用域 developmentOnly 下,bootTestRun 任务将无法检测到您代码中的更改,因为开发工具(devtools)未处于激活状态。