一、开发时服务
开发时服务提供了在开发应用程序时运行所需的外部依赖项。它们仅应在开发时使用,并在应用程序部署时禁用。
Spring Boot 支持两种开发时服务:Docker Compose 和 Testcontainers。接下来的章节将提供有关它们的更多详细信息。
二、Docker Compose
Docker Compose 是一种流行的技术,可用于定义和管理应用程序所需服务的多个容器。通常在应用程序旁边创建一个 compose.yml 文件,用于定义和配置服务容器。
使用 Docker Compose 的典型工作流程是运行 docker compose up,在应用程序连接到已启动服务的情况下进行开发,然后在完成后运行 docker compose down。
Spring AI 提供了 Spring Boot 自动配置,用于建立与通过 Docker Compose 运行的模型服务或向量存储的连接。要启用它,请将以下依赖项添加到项目的 Maven pom.xml 文件中:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-docker-compose</artifactId>
</dependency>
或添加到 Gradle build.gradle 构建文件中:
groovy
dependencies {
implementation 'org.springframework.ai:spring-ai-spring-boot-docker-compose'
}
请参阅 依赖管理 部分,将 Spring AI BOM 添加到您的构建文件中。
2.1 服务连接
spring-ai-spring-boot-docker-compose 模块提供了以下服务连接工厂:

更多服务连接由 Spring Boot 模块 spring-boot-docker-compose 提供。
可以将 spring-boot-docker-compose 模块包含在项目中,以提供使用 Docker Compose 处理容器的支持。将模块依赖项添加到您的构建中,如下面的 Maven 和 Gradle 清单所示:
Maven
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
Gradle
groovy
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 插件时,配置任务的 classpath 以包含 developmentOnly 配置。
2.2 先决条件
您需要在路径上拥有 docker 和 docker compose(或 docker-compose)CLI 应用程序。最低支持的 Docker Compose 版本是 2.2.0。
2.3 服务连接
服务连接是与任何远程服务的连接。Spring Boot 的自动配置可以使用服务连接的详细信息,并利用它们建立与远程服务的连接。这样做时,连接详细信息优先于任何与连接相关的配置属性。
当使用 Spring Boot 的 Docker Compose 支持时,会建立到容器映射端口的服务连接。
Docker compose 通常以这样一种方式使用,即容器内的端口映射到您计算机上的临时端口。例如,Postgres 服务器可能在容器内使用端口 5432 运行,但在本地映射到一个完全不同的端口。服务连接将始终发现并使用本地映射的端口。
服务连接通过使用容器的镜像名称来建立。目前支持以下服务连接:

2.4 SSL 支持
有些镜像默认启用了 SSL,或者您可能希望为容器启用 SSL 以模拟生产环境。Spring Boot 支持为受支持的服务连接配置 SSL。请注意,您仍然需要自行在容器内运行的服务上启用 SSL,此功能仅在您的应用程序中配置客户端 SSL。
以下服务连接支持 SSL:
-
Cassandra
-
Elasticsearch
-
MongoDB
-
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:
yaml
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'
2.5 自定义镜像
有时您可能需要使用自己的镜像版本来提供服务。只要自定义镜像的行为与标准镜像相同,您就可以使用任何自定义镜像。具体来说,标准镜像支持的任何环境变量也必须在您的自定义镜像中使用。
如果您的镜像使用不同的名称,您可以在 compose.yml 文件中使用标签,以便 Spring Boot 可以提供服务连接。使用名为 org.springframework.boot.service-connection 的标签来提供服务名称。
例如:
yaml
services:
redis:
image: 'mycompany/mycustomredis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.service-connection: redis
2.6 跳过特定容器
如果您在 compose.yml 中定义了不想连接到应用程序的容器镜像,您可以使用标签来忽略它。任何带有 org.springframework.boot.ignore 标签的容器都将被 Spring Boot 忽略。
例如:
yaml
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.ignore: true
2.7 使用特定的 Compose 文件
如果您的 compose 文件不在应用程序的同一目录中,或者文件名不同,您可以在 application.properties 或 application.yaml 中使用 spring.docker.compose.file 来指向不同的文件。属性可以定义为绝对路径或相对于应用程序的路径。
例如:
Properties
properties
spring.docker.compose.file=../my-compose.yml
YAML
yaml
spring:
docker:
compose:
file: "../my-compose.yml"
2.8 等待容器就绪
由 Docker Compose 启动的容器可能需要一些时间才能完全就绪。检查就绪状态的推荐方法是在 compose.yml 文件的服务定义中添加一个 healthcheck 部分。
由于在 compose.yml 文件中省略健康检查配置的情况并不少见,Spring Boot 也会直接检查服务就绪状态。默认情况下,当可以建立到其映射端口的 TCP/IP 连接时,容器被视为就绪。
您可以通过在 compose.yml 文件中添加 org.springframework.boot.readiness-check.tcp.disable 标签来逐个容器地禁用此检查。
例如:
yaml
services:
redis:
image: 'redis:7.0'
ports:
- '6379'
labels:
org.springframework.boot.readiness-check.tcp.disable: true
您还可以在 application.properties 或 application.yaml 文件中更改超时值:
Properties
properties
spring.docker.compose.readiness.tcp.connect-timeout=10s
spring.docker.compose.readiness.tcp.read-timeout=5s
可以使用 spring.docker.compose.readiness.timeout 配置总体超时。
2.9 控制 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 退出时停止它
此外,您可以使用 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
properties
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
YAML
yaml
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"
2.10 激活 Docker Compose 配置文件
Docker Compose 配置文件类似于 Spring 配置文件,它们允许您针对特定环境调整 Docker Compose 配置。如果您想激活特定的 Docker Compose 配置文件,可以在 application.properties 或 application.yaml 文件中使用 spring.docker.compose.profiles.active 属性:
Properties
properties
spring.docker.compose.profiles.active=myprofile
YAML
yaml
spring:
docker:
compose:
profiles:
active: "myprofile"
2.11 在测试中使用 Docker Compose
默认情况下,Spring Boot 的 Docker Compose 支持在运行测试时被禁用。
要在测试中启用 Docker Compose 支持,请将 spring.docker.compose.skip.in-tests 设置为 false。
使用 Gradle 时,您还需要将 spring-boot-docker-compose 依赖项的配置从 developmentOnly 更改为 testAndDevelopmentOnly:
Gradle
groovy
dependencies {
testAndDevelopmentOnly("org.springframework.boot:spring-boot-docker-compose")
}
三、Testcontainers 支持
除了将 Testcontainers 用于集成测试之外,还可以在开发时使用它们。接下来的章节将提供有关此方面的更多详细信息。
3.1 在开发时使用 Testcontainers
这种方法允许开发人员快速启动应用程序所依赖的服务容器,从而无需手动配置数据库服务器等内容。以这种方式使用 Testcontainers 提供了与 Docker Compose 类似的功能,不同之处在于您的容器配置是用 Java 而不是 YAML 编写的。
要在开发时使用 Testcontainers,您需要使用"测试"类路径而不是"主"类路径启动应用程序。这将允许您访问所有声明的测试依赖项,并为您提供一个自然的位置来编写测试配置。
要为您的应用程序创建一个可测试启动的版本,您应该在 src/test 目录中创建一个"Application"类。例如,如果您的主应用程序在 src/main/java/com/example/MyApplication.java 中,您应该创建 src/test/java/com/example/TestMyApplication.java。
TestMyApplication 类可以使用 SpringApplication.from(...) 方法来启动真实的应用程序:
Java
java
import org.springframework.boot.SpringApplication;
public class TestMyApplication {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main).run(args);
}
}
Kotlin
Kotlin
import org.springframework.boot.SpringApplication
class TestMyApplication
fun main(args: Array<String>) {
SpringApplication.from(MyApplication::main).run(*args)
}
您还需要定义要与应用程序一起启动的容器实例。为此,您需要确保已将 spring-boot-testcontainers 模块添加为测试依赖项。完成此操作后,您可以创建一个 @TestConfiguration 类,该类声明您要启动的容器的 @Bean 方法。
您还可以使用 @ServiceConnection 注解您的 @Bean 方法,以创建 ConnectionDetails bean。有关支持的技术详情,请参阅 服务连接 部分。
一个典型的 Testcontainers 配置如下所示:
java
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");
}
}
Kotlin
kotlin
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<Nothing>("neo4j:5")
}
}
容器 bean 的生命周期由 Spring Boot 自动管理。容器将自动启动和停止。
您可以使用 spring.testcontainers.beans.startup 属性来更改容器的启动方式。默认使用顺序启动,但如果您希望并行启动多个容器,也可以选择 parallel。
一旦您定义了测试配置,您可以使用 with(...) 方法将其附加到您的测试启动器:
Java
java
import org.springframework.boot.SpringApplication;
public class TestMyApplication {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main).with(MyContainersConfiguration.class).run(args);
}
}
Kotlin
kotlin
import org.springframework.boot.SpringApplication
class TestMyApplication
fun main(args: Array<String>) {
SpringApplication.from(MyApplication::main).with(MyContainersConfiguration::class.java).run(*args)
}
您现在可以像启动任何常规的 Java main 方法应用程序一样启动 TestMyApplication,以启动您的应用程序及其运行所需的容器。
您可以使用 Maven 目标 spring-boot:test-run 或 Gradle 任务 bootTestRun 从命令行执行此操作。
3.2 在开发时贡献动态属性
如果您想在开发时从您的 Container @Bean 方法贡献动态属性,请定义一个额外的 DynamicPropertyRegistrar bean。注册器应使用 @Bean 方法定义,该方法将作为源从中提取属性的容器作为参数注入。这种安排确保了容器在属性被使用之前已启动。
一个典型的配置如下所示:
Java
java
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);
};
}
}
Kotlin
kotlin
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::getHost)
properties.add("spring.mongodb.port", container::getFirstMappedPort)
}
}
}
只要可能,建议使用 @ServiceConnection,但是,对于尚未支持 @ServiceConnection 的技术,动态属性可以作为一个有用的后备方案。
3.3 导入 Testcontainers 声明类
使用 Testcontainers 时的一个常见模式是将 Container 实例声明为静态字段。通常这些字段直接定义在测试类上。它们也可以声明在父类或测试实现的接口上。
例如,以下 MyContainers 接口声明了 mongo 和 neo4j 容器:
Java
java
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");
}
Kotlin
kotlin
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 {
@JvmField
@Container
@ServiceConnection
val mongoContainer = MongoDBContainer("mongo:5.0")
@JvmField
@Container
@ServiceConnection
val neo4jContainer = Neo4jContainer<Nothing>("neo4j:5")
}
}
如果您已经以这种方式定义了容器,或者您更喜欢这种风格,您可以导入这些声明类,而不是将容器定义为 @Bean 方法。为此,请将 @ImportTestcontainers 注解添加到您的测试配置类:
Java
java
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.context.ImportTestcontainers;
@TestConfiguration(proxyBeanMethods = false)
@ImportTestcontainers(MyContainers.class)
public class MyContainersConfiguration {
}
Kotlin
java
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 注解的方法添加到您的声明类中。
3.4 在开发时将 DevTools 与 Testcontainers 结合使用
当使用 devtools 时,您可以使用 @RestartScope 注解 bean 和 bean 方法。当 devtools 重新启动应用程序时,这些 bean 不会被重新创建。这对于 Container bean 尤其有用,因为它们在应用程序重启后仍能保持其状态。
Java
java
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");
}
}
Kotlin
kotlin
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 未激活。