You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置Spring Boot在@JdbcTest测试类结束后关闭连接池?

这问题我之前帮团队踩过坑,确实是Spring Boot测试上下文隔离带来的典型痛点,咱们一步步拆解优化方案:

问题根源拆解

Spring Boot测试默认会为每个配置不同的@ContextConfiguration创建全新的ApplicationContext,而每个上下文都会初始化独立的DataSource连接池。10+个测试类就意味着10+个连接池,每个池默认还有多个活跃连接,分分钟打满PostgreSQL的max_connections上限,触发"连接过多"的报错。

解决方案从易到难

1. 先做上下文复用(零成本见效)

Spring Boot测试会自动缓存完全一致的上下文,所以咱们可以把公共配置抽成基础测试类,让所有测试类继承它,尽量减少上下文差异:

// 基础测试父类,统一公共配置
@RunWith(SpringRunner.class)
@JdbcTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Sql(scripts = {"/schema-init.sql", "/test-data.sql"})
@ContextConfiguration(classes = {CommonDao.class, CommonService.class})
public abstract class BasePostgresTest {
    // 可以放公共的依赖注入、通用测试方法
}

然后业务测试类只需要补充自己的特定配置:

public class UserDaoTest extends BasePostgresTest {
    // 只需要注入当前测试需要的UserDao
    @Autowired
    private UserDao userDao;

    // 测试方法...
}

只要上下文配置差异不大,Spring会自动复用缓存的上下文,连接池数量会骤减。

2. 限制单个连接池的连接数

在src/test/resources/application-test.properties里给HikariCP加个上限,避免单个池占用太多连接:

# 每个连接池最多3个连接,根据测试需求调整
spring.datasource.hikari.maximum-pool-size=3
spring.datasource.hikari.minimum-idle=1
spring.datasource.hikari.idle-timeout=60000

就算有10个连接池,总连接数也只有30,远低于PostgreSQL默认的100上限。

3. 统一清理连接池(替代复制粘贴的临时代码)

你之前的临时代码是手动保存DataSource,其实可以用Spring的TestExecutionListener统一处理,不用每个测试类复制粘贴:

先写一个自定义监听器:

public class DataSourceCleanupListener extends AbstractTestExecutionListener {
    private static final List<HikariDataSource> DATA_SOURCES = new ArrayList<>();

    // 测试实例初始化时,收集连接池
    @Override
    public void prepareTestInstance(TestContext testContext) throws Exception {
        DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class);
        if (dataSource instanceof HikariDataSource) {
            DATA_SOURCES.add((HikariDataSource) dataSource);
        }
    }

    // 整个测试类执行完后,销毁所有连接池
    @Override
    public void afterTestClass(TestContext testContext) throws Exception {
        for (HikariDataSource ds : DATA_SOURCES) {
            if (!ds.isClosed()) {
                ds.close();
            }
        }
        DATA_SOURCES.clear();
    }
}

然后把监听器加到基础测试类上:

@RunWith(SpringRunner.class)
@JdbcTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Sql(scripts = {"/schema-init.sql", "/test-data.sql"})
@ContextConfiguration(classes = {CommonDao.class})
// 合并默认监听器,加上自定义的清理监听器
@TestExecutionListeners(mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
        listeners = DataSourceCleanupListener.class)
public abstract class BasePostgresTest {
}

这样每个测试类执行完会自动销毁对应的连接池,彻底释放连接。

4. 长期最优方案:用Testcontainers管理PostgreSQL容器

如果团队愿意引入Testcontainers,这是最靠谱的解决方案:它会在测试启动时创建临时的PostgreSQL容器,所有测试类共享这个容器,测试结束后自动销毁,完全避免连接泄漏和上下文隔离的问题,而且测试环境和生产环境100%一致。

配置步骤:

  • 先加Maven依赖:
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>
  • 写Testcontainers配置类:
@TestConfiguration
public class TestcontainersPostgresConfig {
    @Bean
    @ServiceConnection // 自动把容器的连接信息注入到Spring的DataSource
    public PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>("postgres:15-alpine")
                .withDatabaseName("test_db")
                .withUsername("test_user")
                .withPassword("test_pass");
    }
}
  • 改造基础测试类:
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(TestcontainersPostgresConfig.class)
@Sql(scripts = {"/schema-init.sql", "/test-data.sql"})
public abstract class BasePostgresTest {
}

这样所有测试类都会共享同一个PostgreSQL容器,连接池只初始化一次,彻底解决连接过多的问题。

内容的提问来源于stack exchange,提问作者TMTH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:48:05