Spring Boot批量执行测试时抛出PSQLException问题排查
问题诊断与解决方案
核心原因分析
你遇到的32这个限制,来源于Spring Boot Test默认的测试上下文缓存最大容量(32)。当批量运行测试时,Spring会为每个“不同”的测试场景创建独立的ApplicationContext,当缓存的上下文数量达到32后,最早创建的上下文会被自动销毁,连带销毁该上下文内的Hikari连接池。后续测试如果意外引用了已被销毁的连接池(比如HikariPool-1),就会抛出「This connection has been closed」异常。
而单独运行测试时,只会创建少量上下文,不会触发缓存驱逐逻辑,因此不会出现问题。
具体解决方案
1. 扩大Spring测试上下文缓存容量
通过系统属性调整缓存的最大大小,避免早期上下文被销毁:
- 命令行方式:运行测试时添加参数
mvn test -Dspring.test.context.cache.maxSize=100 - pom.xml配置:在surefire插件中固定配置
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <systemPropertyVariables> <spring.test.context.cache.maxSize>100</spring.test.context.cache.maxSize> </systemPropertyVariables> </configuration> </plugin>
2. 确保所有测试复用同一个Spring上下文
检查测试类是否存在导致上下文无法复用的差异化配置:
- 所有测试类应继承同一个抽象类,且子类不要添加额外的
@TestConfiguration、@Profile或@PropertySource注解 - 确保所有测试使用相同的属性配置,避免因属性差异触发新上下文的创建
- 可以在抽象类上添加
@ContextConfiguration统一指定配置类,强制所有子类复用同一上下文
3. 优化Testcontainers与连接池的绑定逻辑
确保PostgreSQLContainer单例与Spring DataSource的生命周期完全绑定:
- 使用
@DynamicPropertySource动态注入容器的JDBC属性,避免硬编码,确保所有上下文使用同一个容器实例:public abstract class AbstractDbTest { private static final PostgreSQLContainer<?> POSTGRES_CONTAINER = new PostgreSQLContainer<>("postgres:15-alpine") .withReuse(true); static { POSTGRES_CONTAINER.start(); } @DynamicPropertySource static void registerDbProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", POSTGRES_CONTAINER::getJdbcUrl); registry.add("spring.datasource.username", POSTGRES_CONTAINER::getUsername); registry.add("spring.datasource.password", POSTGRES_CONTAINER::getPassword); } } - 避免在测试类中手动创建DataSource实例,完全依赖Spring自动配置的DataSource,确保连接池全局复用
4. 排查连接泄漏问题
即使测试不直接操作数据库,也可能存在连接泄漏导致连接池被异常关闭:
- 开启Hikari的调试日志,检查连接的获取与释放情况:
logging.level.com.zaxxer.hikari=DEBUG - 确认抽象类中的初始化逻辑是否正确释放了数据库连接,比如是否有未关闭的JdbcTemplate或EntityManager实例
内容的提问来源于stack exchange,提问作者Abdulrahman Humayed
相关产品推荐
相关产品推荐

