0

0

Testcontainers中非Spring应用动态加载数据库连接属性的教程

心靈之曲

心靈之曲

发布时间:2025-11-29 15:22:02

|

422人浏览过

|

来源于php中文网

原创

testcontainers中非spring应用动态加载数据库连接属性的教程

本教程旨在解决在Testcontainers集成测试中,非Spring Java应用如何动态获取并使用PostgreSQL数据库容器的连接信息。核心方法是通过Testcontainers的网络功能和依赖管理,使应用容器与数据库容器在同一网络中通信,并利用网络别名和固定端口构建JDBC URL,从而避免了静态属性文件和Spring `@DynamicPropertySource`的限制。

引言:非Spring应用在Testcontainers中的集成测试挑战

在Java应用的集成测试中,Testcontainers提供了一种便捷的方式来启动真实的服务(如数据库、消息队列等)作为Docker容器。然而,对于不使用Spring框架的应用,当需要在测试运行时动态获取这些容器的连接信息(例如JDBC URL)并将其注入到应用容器中时,会遇到一些挑战。

典型的场景是:一个Java应用需要连接PostgreSQL数据库。在Testcontainers测试中,我们可能启动一个PostgreSQL容器和一个应用容器。问题在于,PostgreSQL容器启动后才能确定其JDBC URL(尤其是随机映射的端口),而应用容器可能需要这个URL才能正确启动。如果应用依赖于一个静态的datasource.properties文件,并且该文件需要在PostgreSQL容器启动后动态生成,那么应用容器的启动时机就成了一个问题。对于非Spring应用,也无法利用Spring Boot的@DynamicPropertySource注解来动态注入属性。

本文将介绍一种基于Testcontainers原生网络和依赖管理机制的解决方案,以优雅地解决这一问题。

核心策略:网络隔离与依赖管理

解决上述问题的关键在于Testcontainers提供的两大功能:容器网络和容器依赖。

X Detector
X Detector

最值得信赖的多语言 AI 内容检测器

下载
  1. 容器网络(Container Network):Testcontainers允许创建自定义的Docker网络,并将多个容器连接到这个网络中。在同一个网络中的容器可以通过它们的网络别名(hostname)和内部端口相互通信,而无需关心宿主机上随机映射的端口。
  2. 容器依赖(Container Dependency):通过dependsOn()方法,可以明确指定一个容器的启动依赖于另一个容器。这确保了依赖的容器(如数据库)会在被依赖的容器(如应用)启动之前完全就绪。

结合这两点,我们可以让应用容器在PostgreSQL容器启动后,通过一个预定义的网络别名和标准端口来访问数据库,从而无需动态生成和加载属性文件。

实现步骤与示例代码

以下是如何在Testcontainers中实现这一策略的详细步骤和示例代码:

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;

public class ApplicationIntegrationTest {

    public static void main(String[] args) {
        // 1. 创建一个共享的Docker网络
        Network network = Network.newNetwork();

        // 2. 启动PostgreSQL数据库容器
        //    - 指定Docker镜像
        //    - 将其连接到共享网络
        //    - 设置网络别名,应用容器将通过此别名访问数据库
        PostgreSQLContainer postgres = new PostgreSQLContainer<>(DockerImageName.parse("postgres:15"))
            .withNetwork(network)
            .withNetworkAliases("postgres"); // 数据库在网络中的别名

        // 3. 启动应用容器
        //    - 指定Docker镜像
        //    - 将其连接到共享网络
        //    - 暴露应用端口(如果需要外部访问)
        //    - 声明对PostgreSQL容器的依赖,确保数据库先启动
        //    - 通过环境变量将JDBC URL注入到应用容器中
        GenericContainer app = new GenericContainer<>(DockerImageName.parse("my-app:0.0.1"))
            .withNetwork(network)
            .withExposedPorts(8080) // 假设应用暴露8080端口
            .dependsOn(postgres)
            // 关键:在应用容器启动前,通过环境变量传递JDBC URL
            // 应用内部需要配置为从这个环境变量读取数据库连接信息
            .withEnv("JDBC_URL", "jdbc:postgresql://postgres:5432/test")
            .withEnv("DB_USERNAME", postgres.getUsername()) // 也可以传递用户名
            .withEnv("DB_PASSWORD", postgres.getPassword()); // 也可以传递密码

        // 4. 启动容器
        //    由于设置了dependsOn,Testcontainers会确保postgres先启动
        postgres.start();
        app.start();

        // 此时,应用容器已启动,并且能够通过 "jdbc:postgresql://postgres:5432/test" 访问数据库。
        // 你可以在这里执行你的集成测试逻辑,例如:
        // RestAssured.given().port(app.getMappedPort(8080)).get("/health").then().statusCode(200);

        // 5. 测试结束后,停止容器
        // app.stop();
        // postgres.stop();
        // network.close(); // 关闭网络
    }
}

代码解析:

  1. Network network = Network.newNetwork();: 创建了一个独立的Docker网络。所有连接到这个网络的容器都可以在彼此之间进行内部通信。
  2. PostgreSQLContainer> postgres = ... .withNetwork(network).withNetworkAliases("postgres");: 启动PostgreSQL容器并将其加入到创建的网络中。withNetworkAliases("postgres")为数据库容器在网络内部设置了一个可解析的别名postgres。
  3. GenericContainer> app = ... .withNetwork(network).dependsOn(postgres);: 启动应用容器,同样将其加入到同一网络中。dependsOn(postgres)确保了postgres容器会在app容器启动之前完全就绪。
  4. app.withEnv("JDBC_URL", "jdbc:postgresql://postgres:5432/test");: 这是关键一步。在应用容器启动之前,我们通过withEnv()方法向其注入了一个名为JDBC_URL的环境变量。这个URL使用了数据库容器的网络别名postgres和PostgreSQL的默认端口5432。注意: 你的Java应用(my-app:0.0.1)内部必须配置为从名为JDBC_URL(或其他你定义的名称)的环境变量中读取其数据库连接字符串。例如,如果你的应用使用System.getenv("JDBC_URL")来获取连接信息,那么这种方式就能完美工作。

注意事项与最佳实践

  • 内部通信与外部访问的区别
    • 在同一个Testcontainers网络内部,容器之间应使用网络别名(如postgres)和标准端口(如5432)进行通信。
    • 如果需要从宿主机(即你的测试代码)直接访问数据库容器,则应使用postgres.getJdbcUrl()或postgres.getMappedPort(5432)获取的随机映射端口和宿主机IP。但对于应用容器与数据库容器之间的通信,这种方式是不必要的,也不推荐。
  • 应用配置:确保你的非Spring Java应用能够从环境变量、命令行参数或类似机制中读取数据库连接信息。GenericContainer提供了withEnv()(设置环境变量)、withCommand()(设置容器启动命令)等方法,可以灵活地将动态信息传递给应用。
  • 资源清理:在测试完成后,确保调用stop()方法来停止容器,并关闭网络(network.close()),以释放Docker资源。通常,这可以通过JUnit的@AfterAll或类似的生命周期钩子来完成。
  • 镜像版本管理:始终指定明确的Docker镜像版本(如postgres:15),以确保测试环境的稳定性和可重复性。

总结

通过利用Testcontainers的容器网络和依赖管理功能,我们为非Spring Java应用提供了一种健壮且灵活的方式来在集成测试中动态连接数据库容器。这种方法避免了传统静态属性文件的限制,也无需依赖Spring框架特定的动态属性机制。通过将容器置于同一网络并使用网络别名,以及通过环境变量注入连接信息,我们可以构建出更可靠、更真实的集成测试环境。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

737

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号