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

Spring Boot如何动态配置多个Hikari数据源?

动态配置多环境下的Hikari数据源问题

单个Hikari数据源的正常配置

如果只需要配置单个Hikari数据源,直接在application.yml中配置即可:

spring:
  datasource:
    app:
      platform: POSTGRESQL
      url: jdbc:postgresql://localhost:5432/mydb
      username: mydbuser
      password: mydbpass
      hikari:
        maximum-pool-size: 15
        leak-detection-threshold: 15000
        max-lifetime: 900000
        data-source-properties: stringtype=unspecified
        pool-name: defaultHikariPool

对应的Kotlin配置类代码如下:

@Bean
@Primary
@ConfigurationProperties("spring.datasource.app")
fun appDataSourceProperties(): DataSourceProperties {
    return DataSourceProperties()
}

@Bean
@ConfigurationProperties(prefix = "spring.datasource.app.hikari")
fun appDataSource(): HikariDataSource {
    return appDataSourceProperties()
        .initializeDataSourceBuilder()
        .type(HikariDataSource::class.java)
        .build()
}

这种方式可以正常运行,但当数据源数量随部署环境动态变化时,就需要另一种实现方式。

动态多数据源的配置困境

针对数据源数量动态变化的场景,我在application.yml中定义了自定义配置块:

my-custom-parties:
  parties:
    - my-custom-party-id: 1
      datasource:
        platform: POSTGRESQL
        url: jdbc:postgresql://localhost:5432/my-db-1
        username: user1
        password: pass1
        hikari:
          pool-name: db1HikariPool
          maximum-pool-size: 3
          leak-detection-threshold: 5000
          max-lifetime: 900000
          data-source-properties: stringtype=unspecified
    - my-custom-party-id: 2
      datasource:
        platform: POSTGRESQL
        url: jdbc:postgresql://localhost:5432/my-db-2
        username: user2
        password: pass2
        hikari:
          pool-name: db2HikariPool
          maximum-pool-size: 10
          leak-detection-threshold: 20000
          max-lifetime: 900000
          data-source-properties: stringtype=unspecified

同时编写了对应的配置类:

@Component
@ConfigurationProperties(prefix = "my-custom-parties")
class MyCustomParties {
    private var parties: List<MyCustomParty> = listOf()

    fun setParties(parties: List<MyCustomParty>) {
        this.parties = parties
    }

    fun getParties() = parties
}

class MyCustomParty {
    lateinit var myCustomPartyId: String
    private var datasource: DataSourceProperties? = null

    fun setDatasource(datasource: DataSourceProperties) {
        this.datasource = datasource
    }

    fun getDatasource() = datasource?.initializeDataSourceBuilder()?.type(HikariDataSource::class.java)?.build()
}

现在的问题是:代码能初始化Hikari数据源,但无法读取配置文件中的Hikari配置项,而且因为数据源数量随环境变化,不能给每个数据源硬编码@Bean。

正确实现动态配置的方案

1. 拆分配置绑定,单独承接Hikari属性

DataSourceProperties不会自动绑定hikari节点下的配置,所以需要单独定义一个类来承接Hikari的配置项:

class HikariCustomProperties {
    var poolName: String? = null
    var maximumPoolSize: Int? = null
    var leakDetectionThreshold: Long? = null
    var maxLifetime: Long? = null
    var dataSourceProperties: String? = null
}

然后修改MyCustomParty类,让它同时持有数据源基础配置和Hikari专属配置,并在构建数据源时手动应用Hikari配置:

class MyCustomParty {
    lateinit var myCustomPartyId: String
    private var datasource: DataSourceProperties? = null
    private var hikari: HikariCustomProperties? = null

    fun setDatasource(datasource: DataSourceProperties) {
        this.datasource = datasource
    }

    fun setHikari(hikari: HikariCustomProperties) {
        this.hikari = hikari
    }

    fun getDatasource(): HikariDataSource? {
        val dataSource = datasource?.initializeDataSourceBuilder()?.type(HikariDataSource::class.java)?.build()
        hikari?.let { hikariProps ->
            dataSource?.apply {
                // 逐个设置Hikari配置项
                hikariProps.poolName?.let { poolName = it }
                hikariProps.maximumPoolSize?.let { maximumPoolSize = it }
                hikariProps.leakDetectionThreshold?.let { leakDetectionThreshold = it }
                hikariProps.maxLifetime?.let { maxLifetime = it }
                // 处理数据源属性字符串
                hikariProps.dataSourceProperties?.let { dsPropsStr ->
                    dsPropsStr.split("&").forEach { prop ->
                        val (key, value) = prop.split("=")
                        addDataSourceProperty(key, value)
                    }
                }
            }
        }
        return dataSource
    }
}

2. 动态注册数据源到Spring容器(可选)

如果需要在Spring中通过依赖注入使用这些动态数据源,可以实现BeanDefinitionRegistryPostProcessor来动态注册Bean:

@Component
class DynamicDataSourceRegistrar(
    private val myCustomParties: MyCustomParties
) : BeanDefinitionRegistryPostProcessor {

    override fun postProcessBeanDefinitionRegistry(registry: BeanDefinitionRegistry) {
        myCustomParties.parties.forEach { party ->
            val dataSource = party.getDatasource() ?: return@forEach
            // 定义Bean名称,格式为dataSource-{partyId}
            val beanName = "dataSource-${party.myCustomPartyId}"
            val beanDefinition = GenericBeanDefinition().apply {
                beanClass = HikariDataSource::class.java
                scope = ConfigurableBeanFactory.SCOPE_SINGLETON
                // 将构建好的数据源作为构造参数传入
                addConstructorArgValue(dataSource)
            }
            registry.registerBeanDefinition(beanName, beanDefinition)
        }
    }

    override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) {
        // 无额外处理逻辑
    }
}

这样Spring启动时会自动读取配置文件中的所有数据源配置,构建对应的HikariDataSource并注册为Bean,后续可以通过@Qualifier("dataSource-1")这样的方式注入指定的数据源。

关键说明

  • Spring Boot配置绑定默认支持短横线命名转驼峰,比如配置文件中的maximum-pool-size会自动映射到Kotlin类的maximumPoolSize属性。
  • 如果不需要将数据源注册为Spring Bean,直接通过MyCustomParties.getParties()获取每个party的数据源即可使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:05:25