|
此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Boot 4.0.4! |
开发时服务
开发时服务提供在开发应用程序期间运行所需的各种外部依赖。 这些服务仅应在开发过程中使用,当应用程序部署后即被禁用。
Spring Boot 提供对两种开发时服务的支持:Docker Compose 和 Testcontainers。 下一节将详细介绍这两项服务。
Docker Compose 支持
Docker Compose 是一项广受欢迎的技术,可用于定义和管理应用程序所需服务的多个容器。
通常会在应用程序旁边创建一个 docker-compose.yml 文件,用于定义和配置服务容器。
使用 Docker Compose 的典型工作流程是:先运行 docker compose up,然后在应用程序中连接已启动的服务进行开发,完成后运行 docker compose down。
spring-boot-docker-compose 模块可被引入项目中,以提供对使用 Docker Compose 管理容器的支持。
请按以下 Maven 和 Gradle 构建配置示例所示,在构建文件中添加该模块依赖项:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
dependencies {
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
}
当此模块作为依赖项引入时,Spring Boot 将执行以下操作:
-
在您的工作目录中搜索
compose.yml及其他常见的 Compose 文件名 -
使用发现的
compose.yml调用docker compose up -
为每个受支持的容器创建服务连接 Bean
-
应用程序关闭时调用
docker compose stop
如果在启动应用程序时 Docker Compose 服务已处于运行状态,Spring Boot 将仅为每个受支持的容器创建服务连接 Bean。
它不会再次调用 docker compose up,且在应用程序关闭时也不会调用 docker compose stop。
重新打包的归档文件默认不包含 Spring Boot 的 Docker Compose 支持。
如需使用此功能,您需要显式引入该支持。
若使用 Maven 插件,请将 excludeDockerCompose 属性设置为 false。
若使用 Gradle 插件,请配置任务的类路径以包含 developmentOnly 配置。 |
前提条件
您需要将 docker 和 docker compose(或 docker-compose)CLI 应用程序添加到您的系统路径中。
支持的最低 Docker Compose 版本为 2.2.0。
服务连接
服务连接是指与任何远程服务的连接。 Spring Boot 的自动配置可以读取服务连接的详细信息,并利用这些信息建立与远程服务的连接。 在此过程中,连接详细信息的优先级高于任何与连接相关的配置属性。
使用 Spring Boot 的 Docker Compose 支持时,服务连接将建立到容器所映射的端口。
| Docker Compose 通常以这种方式使用:将容器内部的端口映射到您计算机上的临时端口。 例如,PostgreSQL 服务器可能在容器内使用端口 5432 运行,但在本地被映射到一个完全不同的端口。 服务连接将始终自动发现并使用该本地映射的端口。 |
服务连接通过使用容器的镜像名称来建立。 当前支持以下服务连接:
| 连接详情 | 匹配于 |
|---|---|
名为“symptoma/activemq”或“apache/activemq-classic”的容器 |
|
名为"rabbitmq"的容器 |
|
名为“apache/activemq-artemis”的容器 |
|
名为"cassandra"的容器 |
|
名为"elasticsearch"的容器 |
|
名为“hazelcast/hazelcast”的容器。 |
|
名为"clickhouse/clickhouse-server"、"gvenzl/oracle-free"、"gvenzl/oracle-xe"、"mariadb"、"mssql/server"、"mysql"或"postgres"的容器 |
|
名为“osixia/openldap”和“lldap/lldap”的容器 |
|
名为"mongo"的容器 |
|
名为"neo4j"的容器 |
|
名为“otel/opentelemetry-collector-contrib”和“grafana/otel-lgtm”的容器 |
|
名为“otel/opentelemetry-collector-contrib”和“grafana/otel-lgtm”的容器 |
|
名为“otel/opentelemetry-collector-contrib”和“grafana/otel-lgtm”的容器 |
|
名为“apachepulsar/pulsar”的容器 |
|
名为"clickhouse/clickhouse-server"、"gvenzl/oracle-free"、"gvenzl/oracle-xe"、"mariadb"、"mssql/server"、"mysql"或"postgres"的容器 |
|
名为 "rabbitmq" 的容器,其中容器端口 5672 被映射 |
|
名为 "rabbitmq" 的容器,其中容器端口 5552 被映射 |
|
名为"redis"、"redis/redis-stack"或"redis/redis-stack-server"的容器 |
|
名为“openzipkin/zipkin”的容器。 |
SSL 支持
某些镜像开箱即用支持 SSL,或者您可能希望为容器启用 SSL,以模拟您的生产环境配置。 Spring Boot 支持对已支持的服务连接进行 SSL 配置。 请注意,您仍需自行在容器内运行的服务上启用 SSL;此功能仅在您的应用程序中配置客户端侧的 SSL。
以下服务连接支持 SSL:
-
卡桑德拉
-
Elasticsearch
-
MongoDB
-
RabbitMQ
-
RabbitMQ 流
-
Redis
要为服务启用SSL支持,您可以使用服务标签。
对于基于 JKS 的密钥库和信任库,您可以使用以下容器标签:
-
org.springframework.boot.sslbundle.jks.key.alias -
org.springframework.boot.sslbundle.jks.key.password -
org.springframework.boot.sslbundle.jks.options.ciphers -
org.springframework.boot.sslbundle.jks.options.enabled-protocols -
org.springframework.boot.sslbundle.jks.protocol -
org.springframework.boot.sslbundle.jks.keystore.type -
org.springframework.boot.sslbundle.jks.keystore.provider -
org.springframework.boot.sslbundle.jks.keystore.location -
org.springframework.boot.sslbundle.jks.keystore.password -
org.springframework.boot.sslbundle.jks.truststore.type -
org.springframework.boot.sslbundle.jks.truststore.provider -
org.springframework.boot.sslbundle.jks.truststore.location -
org.springframework.boot.sslbundle.jks.truststore.password
这些标签与SSL 绑定包可用的属性相对应。
对于基于 PEM 的密钥库和信任库,您可以使用以下容器标签:
-
org.springframework.boot.sslbundle.pem.key.alias -
org.springframework.boot.sslbundle.pem.key.password -
org.springframework.boot.sslbundle.pem.options.ciphers -
org.springframework.boot.sslbundle.pem.options.enabled-protocols -
org.springframework.boot.sslbundle.pem.protocol -
org.springframework.boot.sslbundle.pem.keystore.type -
org.springframework.boot.sslbundle.pem.keystore.certificate -
org.springframework.boot.sslbundle.pem.keystore.private-key -
org.springframework.boot.sslbundle.pem.keystore.private-key-password -
org.springframework.boot.sslbundle.pem.truststore.type -
org.springframework.boot.sslbundle.pem.truststore.certificate -
org.springframework.boot.sslbundle.pem.truststore.private-key -
org.springframework.boot.sslbundle.pem.truststore.private-key-password
这些标签与SSL 绑定包可用的属性相对应。
以下示例为 Redis 容器启用 SSL:
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'
自定义镜像
有时,您可能需要使用自己的镜像来提供服务。 只要您的自定义镜像的行为方式与标准镜像一致,即可使用任意自定义镜像。 具体而言,标准镜像所支持的任何环境变量,也必须在您的自定义镜像中使用。
如果您的镜像使用了不同的名称,您可以在 compose.yml 文件中使用标签,以便 Spring Boot 能够提供服务连接。
请使用名为 org.springframework.boot.service-connection 的标签来指定服务名称。
例如:
services:
redis:
image: 'mycompany/mycustomredis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.service-connection: redis
跳过特定容器
如果在您的 compose.yml 中定义了一个容器镜像,且您不希望该镜像与您的应用程序关联,则可以使用标签将其忽略。
任何带有 org.springframework.boot.ignore 标签的容器都将被 Spring Boot 忽略。
例如:
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.ignore: true
使用特定的 Compose 文件
如果您的 Compose 文件未与应用程序位于同一目录下,或者文件名不同,则可以在您的 spring.docker.compose.file 或 application.properties 中使用 application.yaml 指向其他文件。
属性可定义为绝对路径,也可定义为相对于应用程序的相对路径。
例如:
-
Properties
-
YAML
spring.docker.compose.file=../my-compose.yml
spring:
docker:
compose:
file: "../my-compose.yml"
等待容器就绪
由 Docker Compose 启动的容器可能需要一些时间才能完全就绪。
推荐的就绪状态检查方法是在您的 compose.yml 文件的服务定义下添加一个 healthcheck 部分。
由于在 compose.yml 文件中省略 healthcheck 配置的情况并不少见,Spring Boot 还会直接检查服务是否就绪。
默认情况下,当能够通过 TCP/IP 连接到容器映射的端口时,即认为该容器已就绪。
您可以通过在 compose.yml 文件中添加一个 org.springframework.boot.readiness-check.tcp.disable 标签,按容器单独禁用此功能。
例如:
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.readiness-check.tcp.disable: true
您还可以在 application.properties 或 application.yaml 文件中修改超时值:
-
Properties
-
YAML
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 进行配置。
控制 Docker Compose 生命周期
默认情况下,Spring Boot 在应用程序启动时调用 docker compose up,在关闭时调用 docker compose stop。
如果您希望采用不同的生命周期管理方式,可以使用 spring.docker.compose.lifecycle-management 属性。
以下值受支持:
-
none- 不启动也不停止 Docker Compose -
start-only- 应用程序启动时启动 Docker Compose 并使其持续运行 -
start-and-stop- 应用程序启动时启动 Docker Compose,JVM 退出时停止 Docker Compose
此外,您还可以使用 spring.docker.compose.start.command 属性来更改是使用 docker compose up 还是 docker compose start。
spring.docker.compose.stop.command 允许您配置是使用 docker compose down 还是 docker compose stop。
您还可以向 Docker Compose 命令传递额外参数。
spring.docker.compose.arguments 属性允许您指定传递给所有 Docker Compose 命令的参数。
spring.docker.compose.start.arguments 属性允许您指定仅传递给 up(或 start)命令的参数,而 spring.docker.compose.stop.arguments 属性则允许您指定仅传递给 down(或 stop)命令的参数。
以下示例展示了如何配置生命周期管理:
-
Properties
-
YAML
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.active 或 application.properties 文件中使用 application.yaml 属性:
-
Properties
-
YAML
spring.docker.compose.profiles.active=myprofile
spring:
docker:
compose:
profiles:
active: "myprofile"
在测试中使用 Docker Compose
默认情况下,Spring Boot 的 Docker Compose 支持在运行测试时被禁用。
如需在测试中启用 Docker Compose 支持,请将 spring.docker.compose.skip.in-tests 设置为 false。
使用 Gradle 时,您还需将 spring-boot-docker-compose 依赖项的配置从 developmentOnly 更改为 testAndDevelopmentOnly:
dependencies {
testAndDevelopmentOnly("org.springframework.boot:spring-boot-docker-compose")
}
Testcontainers 支持
除了使用 Testcontainers 进行集成测试之外,还可以在开发阶段使用它们。 接下来的章节将对此进行更详细的介绍。
在开发阶段使用 Testcontainers
这种方案使开发者能够快速启动应用程序所依赖服务的容器,从而无需手动配置数据库服务器等组件。 以这种方式使用 Testcontainers 提供的功能与 Docker Compose 类似,区别在于容器配置采用 Java 编写,而非 YAML 格式。
在开发阶段使用 Testcontainers 时,您需要使用“test”类路径(而非“main”类路径)来启动应用程序。 这将使您能够访问所有已声明的测试依赖项,并为您提供一个编写测试配置的自然位置。
要创建应用程序的可测试启动版本,您应在 src/test 目录中创建一个“Application”类。
例如,如果您的主应用程序位于 src/main/java/com/example/MyApplication.java 中,则应创建 src/test/java/com/example/TestMyApplication.java。
TestMyApplication 类可使用 SpringApplication.from(…) 方法启动实际应用程序:
-
Java
-
Kotlin
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 方法。
您还可以使用 @ServiceConnection 注解您的 @Bean 方法,以创建 ConnectionDetails 类型的 Bean。
有关受支持技术的详细信息,请参阅 服务连接 章节。
典型的 Testcontainers 配置如下所示:
-
Java
-
Kotlin
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上:
-
Java
-
Kotlin
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,以启动您的应用程序及其运行所需的容器。
您可以使用 Maven 目标 spring-boot:test-run 或 Gradle 任务 bootTestRun 从命令行执行此操作。 |
在开发时提供动态属性
如果希望在开发阶段通过您的 Container @Bean 方法提供动态属性,请额外定义一个 DynamicPropertyRegistrar Bean。
注册器应通过一个 @Bean 方法进行定义,该方法将作为参数注入属性来源的容器。
此配置可确保在使用属性之前,容器已启动完毕。
典型的配置如下所示:
-
Java
-
Kotlin
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 实例声明为静态字段。
这些字段通常直接定义在测试类上。
它们也可以声明在父类中,或声明在测试类所实现的接口中。
例如,以下 MyContainers 接口声明了 mongo 和 neo4j 容器:
-
Java
-
Kotlin
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 注解:
-
Java
-
Kotlin
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 尤为有用,因为它们能在应用重启后保持自身状态。
-
Java
-
Kotlin
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)未处于激活状态。 |